Spring小知识(01)关于SpringAop及源码中的一些问题

一、关于SpringAop的一些问题

1. BeanFactoryPostProcessor和BeanPostProcessor的区别?

BeanFactoryPostProcessor实现该接口,==可以在spring的bean创建之前,修改bean的定义属性。==也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,

  • 例如可以把bean的scope从singleton改为prototype,
  • 也可以把property的值给修改掉。
  • 可以同时配置多个BeanFactoryPostProcessor,
  • 并通过设置order属性来控制各个BeanFactoryPostProcessor的执行次序。

BeanPostProcessorBeanPostProcessor,可以在spring容器实例化bean之后,==在执行bean的初始化方法前后,添加一些自己的处理逻辑。==这里说的初始化方法,指的是下面两种:

BeanPostProcessor是在spring容器加载了bean的定义文件并且实例化bean之后执行的。BeanPostProcessor的执行顺序是在BeanFactoryPostProcessor之后

2. JDK安全验证机制?

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的创建文件的方法进行创建文件了。

3. init-method 和@PostConstruct?

@PostConstruct是javax的内容,是Java提案的规范。init-method是Spring中Bean的生命周期,在初始化bean时调用的。其实@PostConstructinit-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中使用。

扫描二维码关注公众号,回复: 9731704 查看本文章
@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");
	}
}

4. Advised、Advice、Advisor?

Advised:包含所有的Advisor 和 Advice

Advice:通知,前置通知,后置通知,环绕通知。指的就是Advice

Advisor:通知 + 切入点的适配器

5 Facets和Artifacts的区别:

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目录,然后把编译后的文件放进去。

6. AspectJ和Sprig AOP

SpringAop 早期也实现了AOP理念,但是由于语法过于复杂,不如@AspectJ简单。所以后来Spring提供了@AspectJ support,也就是说Spring AOP并不是借助了@AspectJ来实现的AOP,只是把原来的语法弄得跟@AspectJ一样简单。

  • 所以要使用SpringAOP时,要加 @EnableAspectJAutoProxy这样一个注解来启用@AspectJ

  • 在使用SpringAOP的时候要导入aspectj的jar包,但是并没用它的底层。只使用了它的语法风格

  • @EnableAspectJAutoProxy这个注解中有个proxyTargetClass属性,默认值为false,为true的时候无论目标对象有无接口,都会使用CGLIB做代理。

6.1 为什么添加了@EnableAspectJAutoProxySpring才有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切面织入.

7. aop的表达式

wthin 只描述到类。

execution 所有的粒度都能描述execution(* com.xyz.service.AccountService.*(..))

@annotation对方法进行aop织入,使用注解方式

@wthin对类进行织入,使用注解方式

this限制连接点匹配 AOP 代理的 bean 引用为指定类型的类

@this限制连接点匹配 AOP 代理的 bean的注解为指定类型的注解

target限制连接点匹配目标对象为指定类型的类。

@target限制连接点匹配目标对象有指定的注解。

8. 关于初始化getBean和主动调用getBean

初始化getBean调用的是 AbstractBeanFactorydoGetBean方法。而 主动调用getBean是调用 DefaultListableBeanFactorygetBean方法

但最终都是调用AbstractBeanFactorydoGetBean方法

9. lookup-method

业务应用场景:一个单例模式的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();
}
<!-- 这是2个非单例模式的bean -->
<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生成的动态代理类的方法。

发布了18 篇原创文章 · 获赞 7 · 访问量 363

猜你喜欢

转载自blog.csdn.net/weixin_44074551/article/details/104741055
今日推荐