spring#注解知识巩固
容器部分
通过 java的 vmoptions 可以通过设置-Dkey=value来覆盖系统的环境变量。
@Configuration注解。等价于之前的配置文件,注解在配置类上面。
@Conditional(WindowsCondition.class)注解。可以通过条件决定bean是否被加入容器中,这个注解中需要传入org.springframework.context.annotation.Condition这个接口的实现类,来作为判断依据的条件。
比如下面就是一个条件类的例子,这个类判断是否当前为windows环境:
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { String s = conditionContext.getEnvironment().getProperty("os.name").toLowerCase(); return s.startsWith("windows"); } }
@Import({Red.class,ColorImportSelector.class})注解。第三方包中无法在类上面添加@Component,@Service等注解,可以通过在配置类上面添加@Import({类名,类名})进行导入。
另外在@Import中还可以通过实现import org.springframework.context.annotation.ImportSelector;的类来导入一些bean到容器中,这个ImportSelector接口的selectImports方法返回的是需要被导入容器中的类名称。
import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class ColorImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{"com.haonan.transcactiontest.color.Green","com.haonan.transcactiontest.color.Blue"}; } }
在@Import中还可以通过实现org.springframework.context.annotation.ImportBeanDefinitionRegistrar;接口的类来手动的注册bean到容器中,比如通过如下代码:
import com.haonan.transcactiontest.color.RainBow; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class ColorImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean red = registry.containsBeanDefinition("com.haonan.transcactiontest.color.Red"); boolean blue = registry.containsBeanDefinition("com.haonan.transcactiontest.color.Blue"); if(red && blue){ RootBeanDefinition rainbow = new RootBeanDefinition(RainBow.class); registry.registerBeanDefinition("rainBow", rainbow); } } }
关于BeanFactory:
在spring中实现org.springframework.beans.factory.FactoryBean这个接口得到一个FactoryBean,当把这个工厂注入到spring中,那么,通过applicationContext.getBean("工厂id"),
获取到的是FactoryBean生成的对象,而非工厂本身。如果需要活得工厂本身,需要用applicationContext.getBean("&工厂id");
//////////////////////////////////////////////////////////////////// //file1 工厂bean ColorFactoryBean.java //////////////////////////////////////////////////////////////////// import org.springframework.beans.factory.FactoryBean; public class ColorFactoryBean implements FactoryBean<Color> { @Override public Color getObject() throws Exception { System.out.println("ColorFactoryBean.getObject........");; return new Color(); } @Override public Class<?> getObjectType() { return Color.class; } @Override public boolean isSingleton() { return true; } } /////////////////////////////////////////////////////////////////////// // file2 配置类 MainConfig.java /////////////////////////////////////////////////////////////////////// import com.haonan.transcactiontest.color.ColorFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Bean public ColorFactoryBean colorFactoryBean(){ ColorFactoryBean colorFactoryBean = new ColorFactoryBean(); return colorFactoryBean; } } /////////////////////////////////////////////////////////////////////// // file3 测试类 Test1.java /////////////////////////////////////////////////////////////////////// package com.haonan.transcactiontest.test; import com.haonan.transcactiontest.color.Color; import com.haonan.transcactiontest.color.ColorFactoryBean; import com.haonan.transcactiontest.config.MainConfig; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Test1 { ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class); @Test public void test1() { for (String name : app.getBeanDefinitionNames()) { System.out.println(name); } Color color1 = (Color) app.getBean("colorFactoryBean"); Color color2 = (Color) app.getBean("colorFactoryBean"); ColorFactoryBean factory = (ColorFactoryBean) app.getBean("&colorFactoryBean"); System.out.println(color1); System.out.println(color2); System.out.println(factory); } }
bean的生命周期由容器管理(创建 ---初始化 ---销毁)
单实例:容器启动时候创建(可以通过@Lazy让实例懒加载,用的时候创建)
多实例:每次创建对象的时候创建(容器中的bean默认都是单实例,通过@Scope("prototype")让bean变成原型实例而非单实例)
方法一:
通过查看@Bean注解的定义可以看到可以在使用@Bean注解的时候通过initMethod,和destroyMethod来指定bean的初始化和销毁时候要执行的方法。
初始化方法调用时机(initMethod指定的):
对象创建完成,并赋值好,调用初始化方法。
销毁方法调用时机(destroyMethod来指定):
单实例:容器关闭的时候销毁
多实例:容器不会管理这个bean,请自己销毁。
方法二:
通过让bean实现org.springframework.beans.factory.InitializingBean接口完成初始化逻辑
通过让bean实现org.springframework.beans.factory.DisposableBean接口实现销毁逻辑
方法三:
使用jsr250。
@PostConstruct注解的方法完成属性设置完成后的初始化逻辑
@PreDestroy注解的方法在容器移除bean之前,回调的销毁逻辑
方法四:
实现org.springframework.beans.factory.config.BeanPostProcessor接口,注意需要看这个接口中方法的注释活得更详细的信息。
BeanPostProcessor可以为所有bean进行设置。
关于BeanPostProcessor
//原理 populateBean(beanName,mbd,instanceWrapper);//给bean进行属性赋值 initializeBean{ applyBeanPostProcessorsBeforeInitialization(wrappedBean,beanName); invokeInitMethods(beanName,wrappedBean,mbd);//执行自定义的初始(上面所述的三种)方法 applyBeanPostProcessorsAfterInitialization(wrappedBean,beanName); }
spring底层大量的使用了BeanPostProcessor接口
举例来说,任何实现了org.springframework.context.ApplicationContextAware接口的bean,都会在bean实例化并赋值属性后,执行applyBeanPostProcessorsBeforeInitialization时候,处理到ApplicationContextAwareProcessor时候(这个ApplicationContextAwareProcessor实现了BeanPostProcessor)。ApplicationContextAwareProcessor看当前bean是否实现了Aware,如果当前bean实现了Aware,就判断当前bean实现的是何种Aware,并注入。
再举例来说上述的@PostConstruct,@PreDestroy都是由InitDestroyAnnotationBeanPostProcessor这个BeanPostProcessor处理的。
再举例来说,处理@Autowired的AutowiredAnnotationBeanPostProcessor,也是由于(不只是)实现了BeanPostProcessor。
关于属性赋值:
@Value,
- 可以使用"字面量,字符串,数字"
- 可以使用SpEL,即#{20-2}
- 可以使用环境变量(操作系统变量、配置文件、vmoption -Dkey=value),即${}
至于使用${}加载配置文件的值,可以通过在配置类上面应用注解@PropertySource("classpath:配置文件名称.后缀名"),@PropertySource会把配置文件中定义的键值对保持到环境变量中去,这时候使用${}结合@Value就可以完成注入了。
自动装配
@Autowired(可以用在属性上,set方法上,构造器上,参数前)
1.默认按照类型从容器中找组件;
2.如果找到多个,就按照属性名称作为组件id去容器中查找;
3.如果容器中存在多个同类型的组件,在使用@Autowired时候要加上@Qualifier("组件id")进行指定;
4.@Autowired(required=false)避免找不到报错;
5.如果容器中存在多个同类型的组件,可以使用@Primary指定某个组件是首选的,避免使用@Qualifier。
@Resource
属于jsr250,不支持@Primary,和对required=false
@Inject
属于JSR330,和Autowired功能一样,但是不支持required=false
关于自定义组件想使用spring容器提供的默认组件,只需要实现XXXAware就可以了。
AOP部分
AOP使用的三个步骤(注意业务组件要从容器中获得,否则直接new的实例不会被aop拦截):
- 将业务逻辑组件和切面类都加入容器中,并且告诉Spring哪个是切面类(@Aspect)
- 在切面类中的每一个通知方法上都标注通知注解,告诉Spring何时何地运行(@Pointcut,可抽取公共的切入点)
- 开启基于注解的aop模式(@EnableAspectJAutoProxy,注意springBoot引入相关starter后自动开启)
例子:
//////////////////////////////////////////// //文件1 MathCaculate.java业务组件类,单词好像写错了 /////////////////////////////////////////// package com.haonan.transcactiontest.service; public class MathCaculate { public float div(float a, float b) { return a / b; } } //////////////////////////////////////////// //文件2 LogAspects.java 切面逻辑类 /////////////////////////////////////////// package com.haonan.transcactiontest.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import java.util.Arrays; @Aspect public class LogAspects { @Pointcut("execution(public float com.haonan.transcactiontest.service.MathCaculate.*(..))") public void pointCut() { // 看吧,任意方法,加上@Pointcut可以定义共用的切入点 } @Before("pointCut()") public void logStart(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); System.out.print("LogAspects.before args:"); System.out.println(joinPoint.getSignature().getName() + "参数:" + Arrays.asList(args)); } @After("execution(public float com.haonan.transcactiontest.service.MathCaculate.*(..))") public void logEnd(JoinPoint joinPoint) { System.out.print("LogAspects.logEnd:"); System.out.println(joinPoint.getSignature().getName() + "运行结束"); } /** * 可以以通过returing指定返回值,通过哪个参数传入切面的方法,需要注意的是,Joinpoint必须是第一参数 * * @param joinPoint * @param result */ @AfterReturning(value = "pointCut()", returning = "result") public void logReturing(JoinPoint joinPoint, Object result) { System.out.print("LogAspects.logReturing:"); System.out.println(joinPoint.getSignature().getName() + "返回:" + result); } /** * 可以以通过throwing指定异常值,通过哪个参数传入切面的方法,需要注意的是,Joinpoint必须是第一参数 * * @param joinPoint * @param exception */ @AfterThrowing(value = "pointCut()", throwing = "exception") public void logException(JoinPoint joinPoint, Exception exception) { System.out.print("LogAspects.logException:"); System.out.println(joinPoint.getSignature().getName() + "返回异常:" + exception); } } //////////////////////////////////////////// //文件3 AopConfig.java 配置类 /////////////////////////////////////////// package com.haonan.transcactiontest.config; import com.haonan.transcactiontest.aop.LogAspects; import com.haonan.transcactiontest.service.MathCaculate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @EnableAspectJAutoProxy @Configuration public class AopConfig { @Bean public MathCaculate mathCaculate() { return new MathCaculate(); } @Bean public LogAspects logAspects() { return new LogAspects(); } } //////////////////////////////////////////// //文件4 AopTest.java测试类 /////////////////////////////////////////// package com.haonan.transcactiontest.test; import com.haonan.transcactiontest.config.AopConfig; import com.haonan.transcactiontest.service.MathCaculate; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class AopTest { AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AopConfig.class); @Test public void test1(){ //必须从容器中获取业务组件类,否则不会应用切面 //组件的代理类,才会被切 MathCaculate bean = app.getBean(MathCaculate.class); float result = bean.div(1f, 0f); System.out.println(result); } }
placeholder
浙公网安备 33010602011771号