BeanFactoryPostProcessor
实现该接口,==可以在spring的bean创建之前,修改bean的定义属性。==也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,
order
属性来控制各个BeanFactoryPostProcessor的执行次序。BeanPostProcessor
BeanPostProcessor,可以在spring容器实例化bean之后,==在执行bean的初始化方法前后,添加一些自己的处理逻辑。==这里说的初始化方法,指的是下面两种:
BeanPostProcessor是在spring容器加载了bean的定义文件并且实例化bean之后执行的。BeanPostProcessor的执行顺序是在BeanFactoryPostProcessor之后
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(DefaultListableBeanFactory.this);
return null;
}, getAccessControlContext());
}
SecurityManager应用场景:
当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。
AccessController.doPrivileged的作用:
Java默认不打开安全检查,如果不打开,本地程序拥有所有权限。但是如果程序中加了 System.setSecurityManager(new SecurityManager());
则Java程序会检验权限。
例子:假设有这样一种情况:A程序想在 /Users/apple/Desktop 这个目录中新建一个文件,但是它没有相应的权限,但是它引用了另外一个Jar包B,刚好B有权限在/Users/apple/Desktop目录中新建文件,还有更巧的是B在新建文件的时候采用的是AccessController.doPrivileged
方法进行的,这种情况下,A就可以调用B的创建文件的方法进行创建文件了。
@PostConstruct是javax的内容,是Java提案的规范。init-method是Spring中Bean的生命周期,在初始化bean时调用的。其实@PostConstruct
和 init-method配置
作用效果是一样的,只是使用场景不同。
使用@PostConstruct
只需要加载方法上即可:
@Service(value = "cityService")
public class CityServiceImpl implements CityService {
@PostConstruct
public void beforeConstruct() {
System.out.println("CityService实例创建完成后执行此代码");
}
@PreDestroy
public void destoryConstruct() {
System.out.println("CityService销毁之前执行此代码");
}
}
使用init-method配置
,是在@Bean中使用。
@Bean(name = "t", initMethod = "init", destroyMethod = "destroy")
public TestAtBeanImpl testAtBeanImpl() {
return new TestAtBeanImpl();
}
public class TestAtBeanImpl implements TestAtBean {
@Override
public void testAtBean() {
System.out.println("testAtBean");
}
public void init() {
System.out.println("TestAtBeanImpl-->this is init method1 ");
}
public void destroy() {
System.out.println("TestAtBeanImpl-->this is destroy method1");
}
}
Advised:包含所有的Advisor 和 Advice
Advice
:通知,前置通知,后置通知,环绕通知。指的就是Advice
Advisor:通知 + 切入点的适配器
Facets和Artifacts的区别:
Facets 表示这个module有什么特征,比如 Web,Spring和Hibernate等;
Artifact 是maven中的一个概念,表示某个module要如何打包,例如war exploded、war、jar、ear等等这种打包形式;
一个module有了 Artifacts 就可以部署到应用服务器中了!
在给项目配置Artifacts的时候有好多个type的选项,exploed是什么意思:
explode 在这里你可以理解为展开,不压缩的意思。也就是war、jar等产出物没压缩前的目录结构。建议在开发的时候使用这种模式,便于修改了文件的效果立刻显现出来。
默认情况下,IDEA的 Modules 和 Artifacts 的 output目录 已经设置好了,不需要更改,打成 war包 的时候会自动在 WEB-INF目录 下生产 classes目录,然后把编译后的文件放进去。
SpringAop 早期也实现了AOP理念,但是由于语法过于复杂,不如@AspectJ简单。所以后来Spring提供了@AspectJ support,也就是说Spring AOP并不是借助了@AspectJ来实现的AOP,只是把原来的语法弄得跟@AspectJ一样简单。
所以要使用SpringAOP时,要加 @EnableAspectJAutoProxy
这样一个注解来启用@AspectJ
在使用SpringAOP的时候要导入aspectj的jar包,但是并没用它的底层。只使用了它的语法风格
@EnableAspectJAutoProxy
这个注解中有个proxyTargetClass
属性,默认值为false,为true的时候无论目标对象有无接口,都会使用CGLIB做代理。
@EnableAspectJAutoProxy
Spring才有AOP的功能源码分析,来看 @EnableAspectJAutoProxy
的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(AspectJAutoProxyRegistrar.class) // 导入了AspectJAutoProxyRegistrar这个类。
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
通过源码可以看到,使用注解 @EnableAspectJAutoProxy
就会导入一个类 AspectJAutoProxyRegistrar
,就是这个切面自动代理注册
的类将后置处理器加入到Spring的体系中的,这样Spring在初始化的时候,调用的后置处理器就多了一个 AutoProxyCreator
来执行AOP切面织入.
wthin
只描述到类。
execution
所有的粒度都能描述execution(* com.xyz.service.AccountService.*(..))
@annotation
对方法进行aop织入,使用注解方式
@wthin
对类进行织入,使用注解方式
this
限制连接点匹配 AOP 代理的 bean 引用为指定类型的类
@this
限制连接点匹配 AOP 代理的 bean的注解为指定类型的注解
target
限制连接点匹配目标对象为指定类型的类。
@target
限制连接点匹配目标对象有指定的注解。
初始化getBean调用的是 AbstractBeanFactory
的doGetBean
方法。而 主动调用getBean
是调用 DefaultListableBeanFactory
的getBean
方法
但最终都是调用AbstractBeanFactory
的doGetBean
方法
业务应用场景:
一个单例模式的bean A需要引用另外一个非单例模式的bean B,为了在我们每次引用的时候都能拿到最新的bean B,可以让bean A通过实现ApplicationContextWare来感知applicationContext(即可以获得容器上下文),从而能在运行时通过ApplicationContext.getBean(String beanName)的方法来获取最新的bean B。但是如果用ApplicationContextAware接口,就让我们与Spring代码耦合了,违背了反转控制原则(IoC,即bean完全由Spring容器管理,我们自己的代码只需要用bean就可以了)。
// 定义一个水果类
public class Fruit {
public Fruit() {
System.out.println("I got Fruit");
}
}
// 苹果
public class Apple extends Fruit {
public Apple() {
System.out.println("I got a fresh apple");
}
}
// 香蕉
public class Bananer extends Fruit {
public Bananer () {
System.out.println("I got a fresh bananer");
}
}
// 水果盘,可以拿到水果
public abstract class FruitPlate{
// 抽象方法获取新鲜水果
protected abstract Fruit getFruit();
}
<bean id="apple" class="cn.com.willchen.test.di.Apple" scope="prototype"/>
<bean id="bananer" class="cn.com.willchen.test.di.Bananer " scope="prototype"/>
<bean id="fruitPlate1" class="cn.com.willchen.test.di.FruitPlate">
<lookup-method name="getFruit" bean="apple"/>
bean>
<bean id="fruitPlate2" class="cn.com.willchen.test.di.FruitPlate">
<lookup-method name="getFruit" bean="bananer"/>
bean>
其中,最为核心的部分就是lookup-method的配置和FruitPlate.getFruit()方法。上面代码中,我们可以看到getFruit()方法是个抽象方法,我们并没有实现它啊,那它是怎么拿到水果的呢。这里的奥妙就是Srping应用了CGLIB(动态代理)类库。Spring在初始化容器的时候对配置的bean做了特殊处理,Spring会对bean指定的class做动态代理,代理标签中name属性所指定的方法,返回bean属性指定的bean实例对象。每次我们调用fruitPlate1或者fruitPlate2这2个bean的getFruit()方法时,其实是调用了CGLIB生成的动态代理类的方法。