spring4 知识点
1 bean的 创建
1,直接在配置文件里面写一个带有@Bean注解的方法(返回值就是那个bean对象),(name等于 方法名)
@Bean还可以写在枚举上面
2,使用 FactoryBean 接口(三个方法分别是创建,类型,单例),需要把它加入到spring 容器管理,@Component 或者 @Bean
这时候 默认名字指向的是 FactoryBean 返回的元素对象实例。而不是 FactoryBean 对象的实例,使用 &默认名字 指向的FactoryBean 对象实例
单例模式的时候元素对象放在单例池中,当多例的时候每次通过FactoryBean 实例区获取一个新的单例对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /** * FactoryBean 向Spring容器注册对象 * * @Author ZHANGYUKUN */ @Component ( "goods" ) public class GoodsFactoryBean implements FactoryBean<Goods> { int i = 0 ; /** * 返回一个元素实例 * @return * @throws Exception */ @Override public Goods getObject() throws Exception { Goods goods = Goods.randomGoods(); goods.setName( "goodsName" + i++); return goods; } /** * 返回类工厂元素类型 * @return */ @Override public Class<?> getObjectType() { return Goods. class ; } /** * 选择多例模式 * @return */ @Override public boolean isSingleton() { return true ; } } |
3,在 配置类里面可以 用一个 @Bean 方法的形参里面写入Spring 容器里面的 Bean 对象,这样会自动注入
1 2 3 4 5 6 | @Bean public String testParam( GoodsService getGood1) { System.out.println( "形式参数:" + getGood1 ); return "testParamRT" ; } |
备注:只有一个默认只有满足有且只有一个同类型的对象才能注入,否者随机注入一个,需要精确注入可以使用@Qualifier("name")
2 指定bean 的 初始销毁几种方法
1 bean 类继承 ,InitializingBean,DisposableBean 接口实现对应方法
2, 在配置注解上面指定 @Bean(destroyMethod="destroy",initMethod="afterPropertiesSet")
3,(jsr 250 的 注解) 在方法前写上 @PreDestroy(销毁前) 和 @PostConstruct(创建以后)
调用顺序 java >接口的>@Bean 指定的
1 2 3 4 | @Bean (initMethod = "initMethod" ,destroyMethod = "destroyMethod" ) public BeanLifecycle beanLifecycle( @Qualifier ( "getGood2" ) Goods getGood1) { return new BeanLifecycle(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class BeanLifecycle implements InitializingBean, DisposableBean { @PostConstruct public void postConstruct() throws Exception { System.out.println( "1 postConstruct 方法 " ); } @PreDestroy public void preDestroy() throws Exception { System.out.println( "4 preDestroy 方法" ); } @Override public void afterPropertiesSet() throws Exception { System.out.println( "2 InitializingBean 接口的 afterPropertiesSet 方法 " ); } @Override public void destroy() throws Exception { System.out.println( "5 DisposableBean 接口的 destroy 方法" ); } public void initMethod() throws Exception { System.out.println( "3 initMethod 方法" ); } public void destroyMethod() throws Exception { System.out.println( "6 destroyMethod 方法 " ); } } |
3 spring bean属性的注入
@Primary ,@Autowired ,@Qualifier,@Resource 关系
1 同类的bean有多个实例 ,按照 类型获取的时候会获取代 @Primary 注解标注的那个
2 如果没有指定那么 @Primary那么@Autowired 默认会报错,但是可以通过 @Qualifier("name") 类指定注入实例的名字
1 2 3 | @Autowired @Qualifier ( "getGood1" ) Goods goods; |
3 @Resource 默认就是按照字段名字找优先(没写名字的时候默认名字是字段名字),如果找不到再按照类型,并且也可以指定 字段以外的名字 @Resource(name="getGood1"),但是@Resource 不能写在方法参数上, @Qualifier可以
@Resource,@Qualifier 都可以打破 @Primary的优先注入
4 spring的多例
@Scope("prototype") 修饰的@Bean 或者 @Component 表示这个类是多例对象(spring 管理的对象默认是单例的),或者实现FactoryBean接口
1 2 3 4 5 6 7 8 9 10 | //--------------------------------@Scope的 多例--------------------------------------------------------- int i = 0 ; @Bean @Scope ( "prototype" ) public Goods getScopeGoods() throws Exception { Goods goods = Goods.randomGoods(); goods.setName( "getScopeGoods" + i++); return goods; } |
获取多例有2种办法
1 直接通过名字在容器中获取
1 2 3 4 5 6 7 8 9 | /** * 通过 @Scope 获取多例对象 */ @Test public void t4(){ logger.info( "通过对象名字获取多例1:{}" , SpringContextHoder.getContext().getBean( "getScopeGoods" ) ); logger.info( "通过对象名字获取多例2:{}" , SpringContextHoder.getContext().getBean( "getScopeGoods" ) ); logger.info( "通过对象名字获取多例3:{}" , SpringContextHoder.getContext().getBean( "getScopeGoods" ) ); } |
2 通过 ObjectFactory 获取(注解方式需要用这种)
1 2 3 4 5 6 7 8 9 10 | @Autowired ObjectFactory<Goods> getScopeGoods; /** * 通过ObjectFactory获取多例对象 */ @Test public void t6(){ System.out.println( getScopeGoods.getObject() );; } |
备注: ObjectFactory <T> 方式获取多例对象,最好指定名字,在单例多例并且,并且不只一个的时候可能会找不到不你想要的那个多例对象(@Primary 在不指定名字的时候优先级最高,如果没有指定@Primary的对象,那么ObjectFactory 是有效获取多例对象的),实现factoryBean 多例和@Scope 的多例都可以这样获
比如这样:
1 2 3 | @Autowired @Qualifier ( "goodsFactoryBean" ) ObjectFactory<Goods> getScopeGoods; |
5 AnnotationConfigApplicationContext(Config.class,UserFactoryBean.class) 的 方法里面 不仅仅 可以@Configuration标注的配置类 还可以写 带有注解的组件(@Component ,@Service @Controller @Repository,等等)
5 @Qualifier 对 @Autowired,@Inject(需要引入inject 包) 按照类型匹配进行修饰(按照名字选择)
@ComponentScan 指定扫描的包
备注:@Inject 注解已经现在默认不能用了, 它属于 JSR 330 标准
6 拿到 spring 容器
1 实现 ApplicationContextAware
这个方法其实就是在 BeanPostProcessor 里面判断是否实现了 ApplicationContextAware 接口 ,若果实现了,那么就掉指定方法设置 spring容器
2 构造方法的参数里面写 public SpringContext(ApplicationContext applicationContext,@Qualifier("user2") User user)
备注,这种方法只能有一个构造函数,多个不知道选那个,会默认选择无参数的那个 (如果没有无参数的那个会报错) spring 4.3 提供(spring4.3 默认会给会为 构造方法注入)
3 直接在任意spring 管理的类中注入 spring 容器 @Inject AnnotationConfigApplicationContext context;
4 不能通过getBean 的到
@Bean参数形参注入(貌似会循环注入)
1 2 3 4 5 6 | //--------------------------------@Bean 形参获取 ApplicationContext--------------------------------------------------------- @Bean public ApplicationContext getApplicationContext( ApplicationContext applicationContext) { System.out.println( "通过形参获取applicationContext:" + applicationContext ); return applicationContext; } |
构造方法参数
@Component
public class MySpringApplicationContext {
private ApplicationContext context;
//webApplicationContext是默认容器的名字
public MySpringApplicationContext(@Qualifier("webApplicationContext") ApplicationContext context) {
this.context = context;
}
public ApplicationContext getContext() {
return context;
}
}
测试方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Autowired MySpringApplicationContext mySpringApplicationContext; @Autowired @Qualifier ( "getApplicationContext" ) ApplicationContext applicationContext; /** * 通过构造方法获取 ApplicationContent */ @Test public void t8(){ //通过ApplicationContextAware获取 System.out.println( SpringContextHoder.getContext() ); //通过构造方法获取(@bean的 方法估计也行) System.out.println( mySpringApplicationContext.getContext() ); //直接注入获取 System.out.println( applicationContext ); //这种方式不能获取 System.out.println( mySpringApplicationContext.getContext().getBeansOfType( ApplicationContext. class ) ); } |
7 几个重要的 Factory的接口
BeanFactory,FactoryBean,ObjectFactory
1 BeanFactory spring的工厂类,它不是一种类的工厂(不是只返回一种类型),而是一个容器接口,通过这个容器接口我们可以拿到spring托管的对象。
2 FactoryBean 我们的工厂类,通过这个接口让spring给我们托管这个工厂类,从而产生 工厂里面的 对象(单例一般不需要,使用多例的时候可以借用这个 接口 来产生多例的实例对象)
3 ObjectFactory
多用于多例对象的获取, 用法:
1 2 3 | @Autowired ObjectFactory<单例对象类型> 单例对象工厂; |
8 几个重要的 postProcessor 接口
BeanPostProcessor ,BeanFactoryPostProcessor ,BeanDefinitionRegistryPostProcessor
执行顺序 BeanDefinitionRegistryPostProcessor >BeanFactoryPostProcessor >BeanPostProcessor
1 BeanPostProcessor
实现 BeanPostProcessor 的类可以对任何 spring管理的类的 初始化开始和完成进行监听,并且任意的修改 这个对象。
备注:我们可以通过 postProcessor 对server 类调用 做参数日志(返回一个代理对象,然后在方法调用前打印参数)
备注2: BeanPostProcessor 对所有spring管理的 类都生效。及其强大,能做的拓展及其的多
一个修改 goods 属性的 BeanPostProcessor 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Component public class MyBeanPostProcessor implements BeanPostProcessor { //可以在spring托管的Bean 对象构造完成的前后 做一些处理,比如修改一些属性,甚至环一个实例 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ( beanName.equals( "getGood1" ) ){ Goods goods1 = ((Goods)bean); goods1.setName(goods1.getName() + "postProcessBeforeInitialization附加" ); System.out.println( "postProcessBeforeInitialization:" + beanName + "::" + bean ); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ( beanName.equals( "getGood1" ) ){ Goods goods1 = ((Goods)bean); goods1.setName(goods1.getName() + "postProcessAfterInitialization附加2" ); System.out.println( "postProcessAfterInitialization:" + beanName + "::" + bean ); } return bean; } } |
测试代码,打印修改以后的名字,打印已有的BeanPostProcessor 实现类有几个(加我写的那个一共16个)
1 2 3 4 5 6 7 8 | /** * 测试 BeanPostProcessor 修改对象属性 */ @Test public void t9(){ logger.info( "BeanPostProcessor 修改goods的 名字:{}" , goods.getName() ); logger.info( "BeanPostProcessor 修改goods的 名字:{}" , applicationContext.getBeansOfType(BeanPostProcessor. class ).size() ); } |
2 BeanFactoryPostProcessor
BeanFactoryPostProcessor 接口 可以拿到 所有bean 的 factroy 在 BeanPostProcessor 之前触发,这里获取到的是 spring容器对象,这时候其实没有初始化完成,如果这里获取bean,会导致bean 的提前初始化,在后面 BeanPostProcessor 执行的时候就不会在初始化这个类了(认为已经初始化完成)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | @Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor, BeanPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println( "MyBeanFactoryPostProcessor:" + beanFactory ); //这时候很多容器初始化没完成bean 如果用了,后面 BeanPostProcessor 的流程对应的 bean 会被跳过(估计认为已经初始化完成了),注释掉下面一行以后就会执行 System.out.println( "MyBeanFactoryPostProcessor 获取对象:" + beanFactory.getBean( "myGoods" ) ); } //可以在spring托管的Bean 对象构造完成的前后 做一些处理,比如修改一些属性,甚至环一个实例 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ( beanName.equals( "myGoods" ) ){ Goods goods1 = ((Goods)bean); goods1.setName(goods1.getName() + "postProcessBeforeInitialization附加" ); System.out.println( "MyBeanPostProcessor前:" + "修改参数" ); } System.out.println( "MyBeanPostProcessor前:" + "修改参数:" + "beanName" ); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ( beanName.equals( "myGoods" ) ){ Goods goods1 = ((Goods)bean); goods1.setName(goods1.getName() + "postProcessAfterInitialization附加2" ); System.out.println( "MyBeanPostProcessor后:" + "修改参数" ); } return bean; } |
3 BeanDefinitionRegistryPostProcessor
可以动态的注册spring 的 bean 对象,甚至修改Bean 的定义,Bean的类型,能做的事情比 BeanPostProcessor还多 (BeanDefinitionRegistryPostProcessor 集成自 BeanFactoryPostProcessor 接口)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | @Component public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { //注册一个bean RootBeanDefinition beanDefinition = new RootBeanDefinition(MyGoods. class ); //构造参数 //ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues(); // constructorArgumentValues.addIndexedArgumentValue(1, "user2"); // beanDefinition.setConstructorArgumentValues(constructorArgumentValues); //添加 名字是 myDefinitionObject 的 BeanDifinition registry.registerBeanDefinition( "myDefinitionObject" ,beanDefinition); //查找一个bean(所有的bean都可以在这里找到) System.out.println( "有那些beanDefinition:" + JSONObject.toJSONString( registry.getBeanDefinitionNames() ) ); BeanDefinition myDefinitionObject = registry.getBeanDefinition( "myDefinitionObject" ); //删除bean定义 registry.removeBeanDefinition( "myDefinitionObject" ); //在删除以后修改 myDefinitionObject 的 beanDifinition(类型都改了) RootBeanDefinition beanDefinition2 = new RootBeanDefinition(Goods. class ); registry.registerBeanDefinition( "myDefinitionObject" ,beanDefinition2); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } } |
--------------------------------------------------------------------------------------------------------- 一些杂记------------------------------------------------------------------------------------------------------------------------
spring 的核心
ioc (容器+工厂)
aop ( 代理 + aspectj )
IOC 部分
ApplicationContext 的所有父接口都能干什么?
BeanFactory 的所有接口
BeanFactory ApplicationContext ,创建类实例的时间节点不一样?
xml 的 2种参数注入方式(set注入和 构造方法注入)
xml 向属实里面设置空值,特殊值,内部bean赋值,引用赋值,集合
spring xml p和 util 的namespan
FactoryBean 和 BeanFactory 区别,都是工厂类,前者是 spring的 bean 工厂,后者是spring 提供给我们实现工厂的接口
xml 通过 scope 可以设置 是单例还是多例(singleton,prototype),注解方式需要借助ObjectFactory 来得到多例
bena的生命周期 @PreDestroy,@PostConstruct ,@Bean(initMethod = "init", destroyMethod = "destroy") 在容器关闭的时候的顺序
BeanPostProcessor 里面 bean 的生命周期阶段监听函数
BeanPostProcessor 对所有 spring容器托管的bean都生效
@Resource ,@Autowired,@Qualifier ,Resource 可以指定 name ,Autowired 需要通过 Qualifier 指定名字,Resource 如果没有指定名字,那么变量名字优先,找不到会按照类型匹配,比Autowired 更加精确,所以有部分人推荐使用Resource
xml 方式 的 Autowire 支持 byType 和 byName 2种方式
xml 方式 通过 <context:property-placehoder location="文件.xml" >引入多个 xml 文件,神效时间是全程还是之前引入的才生效?
XMLApplicationContext 里面要使用注解需要 <context:component-scan base-package="">
xml 包扫描的 过滤器 使用自定义过滤器
spring 容器的生命周期接口函数 有哪些
spring纯注解方式使用的 AnnotationConfigApplicationContext 指定配置类来实现的
AOP 部分
spring AOP的 的 两种方式 ,动态代理(需要接口) 和 CGlib织入(修改源码)
JDK的 动态代理
AOP 的 5种切入类型
aspectj
https://blog.csdn.net/java_green_hand0909/article/details/90238242
xml 方式的 切面程序<aop:aspectj-autoproxy >
@Aspect 通过 @order 调整顺序
@EnableAspectjAutoProxy(proxyTargerClass=true)
能耍的时候就一定要耍,不能耍的时候一定要学。
--天道酬勤,贵在坚持posted on 2018-07-10 20:29 zhangyukun 阅读(135) 评论(0) 编辑 收藏 举报
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步