动态代理及 Spring AOP 用法

谈谈 对 Spring 的理解??

这道题是java 面试 最常见的题,上网随便搜都能得到很多答案,其中都提到spring 两个核心特性:IOC 和 AOP。这里谈谈我对AOP的理解。

AOP(Aspect-Oriented Programming),即面向切面编程,平时用到的拦截器,过滤器,都是AOP 思想。AOP思想把系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。利用这种”横切“ 的技术,横切关注点会发生在多处核心关注点,并且各处都基本相似,比如,权限、日志、异常、事务处理等。

AOP 实现原理

动态代理。讲动态代理之前,先看看静态代理的代码:

/**
 * 静态代理演示,接口,目标类,代理类,目标类和代理类实现同一个接口,获得同样技能
 * @author hyl
 *
 */
public class StaticProxy {

	public static void main(String[] args) {
		Person star = new Star();
		Person starProxy = new StaticStarProxy(star);
		starProxy.sing();
	}
}

/**
 * 定义一个接口
 * @author hyl
 *
 */
interface Person{
	void sing();
}

/**
 * 目标类
 * @author hyl
 *
 */
class Star implements Person{

	@Override
	public void sing() {
		System.out.println("我是歌星,我会唱好听的歌");
	}
}

/**
 * 代理类实现同样的接口,不同的是,他不是自己唱歌,而是让歌星唱
 * 也是因为每次要实现同样的接口,如果方法多,每个方法都要维护,所以产生了动态代理
 * @author hyl
 *
 */
class StaticStarProxy implements Person{
	/** 接受要代理的对象*/
	Person person;
	
	public StaticStarProxy(Person person) {
		super();
		this.person = person;
	}

	@Override
	public void sing() {
		System.out.println("我是经纪人,给我钱就让歌星唱歌");
		person.sing();
		
	}
	
} 

上面代码注释也写了,目标类要和代理类实现同样的接口,那么如果接口添加方法,目标类和代理类都要维护。这个时候,动态代理登场,接口和目标类不变,上代码:

/**
 * JDK动态代理,和静态代理一样的角色,一个接口,一个是目标对象,一个代理对象
 * 创建代理对象主要需要两个类 InvocationHandler,Proxy
 * 代理类必须实现InvocationHandler 方法,用Proxy.newProxyInstance创建动态代理对象
 * @author hyl
 *
 */
public class DynamicProxy {

	public static void main(String[] args) {
		
		Person star = new Star();
		InvocationHandler handler = new DynamicStarProxy(star);
		/**创建代理类
		 * 第一个参数 handler.getClass().getClassLoader() ,使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数star.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
		 */
		Person dynamicProxy = (Person) Proxy.newProxyInstance(handler.getClass().getClassLoader(), star.getClass().getInterfaces(), handler);
		/**
		 * 通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,
		 * 它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,
		 * 而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
		 */
		System.out.println("dynamicProxy:  "+dynamicProxy.getClass().getName());
		dynamicProxy.sing();
		
		
	}
}

/**
 * 代理类必须实现InvocationHandler 接口
 * 同时创建一个供传入真是对象的构造方法
 * @author hyl
 *
 */
class DynamicStarProxy implements InvocationHandler{

	// 这个就是我们要代理的真实对象
    private Object subject;
    
    //    构要代理的真实对象赋值
    public DynamicStarProxy(Object subject)
    {
        this.subject = subject;
    }
    
	/**
	 * @param proxy  指代我们所代理的那个真实对象
	 * @param method 调用真实对象的某个方法的Method对象
	 * @param args 方法的参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println(subject.getClass().getName());
		System.out.println("我是经纪人,给我钱才让歌星唱歌。。动态代理");
		Object object =  method.invoke(subject, args);
		System.out.println("歌星唱完歌了,大家鼓掌");
		return object;
	}
	
}

通过代码可以看到,在创建代理对象的时候只需要传入目标对象的所有接口,就能让代理对象可代理所有的接口的所有方法,但是有多个接口的话,创建的时候要强转,比如上面Star 实现了Person,创建的时候转成了Person,如果Star 不仅实现了Person,还实现Monkey,要用到Monkey 类下方法的时候 要强转为Monkey。

Object object =  method.invoke(subject, args);  这句代码是用反射机制,所以动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。上面例子在运行 dynamicProxy.sing(); 时才确认要执行的方法。

这里只写了JDK自带的动态代理,这种代理方式是目标类必须实现接口,如果没有接口,继承,如何解决:CGlib 方式。这里不做笔记。 

AOP 的实现 

第一步,spring配置文件开启识别,添加  <aop:aspectj-autoproxy/>

第二部,定义切面,代码:

/**
 * @Aspect 定义一个切面,@Component注入到spring
 * @author hyl
 *
 */
@Component
@Aspect
public class AopTest {
 
	/**
	 * 声明切点,两部分:
	 * 一个方法签名, 包括方法名和相关参数
 	 * 一个 pointcut 表达式, 用来指定哪些方法执行是要可以织入 advice的
 	 * 满足 point cut 规则的 叫 join point 连接点
	 */
	@Pointcut("execution(* hyl.com.front.controller..*(..))")
	public void log(){}
	
	/**
	 * 定义 advice (增强)
	 * 定义满足表达式的 “拦截器”,增强join point(符合规则的方法)
	 * @param joinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around("log()")
	public Object writeLog(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("AOP之前");
		System.out.println(joinPoint.getSignature().getName());
		Object object = joinPoint.proceed();
		System.out.println("AOP之后");
		return object;
	}
	
}

上面的声明切点和增强可以合在一起写

    @Around("execution(* hyl.com.front.controller..*(..))")
	public Object writeLog(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("AOP之前");
		System.out.println(joinPoint.getSignature().getName());
		Object object = joinPoint.proceed();
		System.out.println("AOP之后");
		return object;
	}

增强常用方法注解:

@around  可以在一个方法的之前之前和之后添加不同的操作。

@Before 方法执行之前执行

@After 方法执行之后执行

@AfterThrowing  方法抛出异常之后执行

@AfterRunning  方法返回结果之后执行

这些注解的用法都和上面代码的@around  一样。

常见切点表达式:

 "execution(* aaa(..))"  表示匹配所有目标类中的 aaa() 方法。

"within(com.hyl.*)"  表示 com.hyl 包中的所有连接点,,即包中的所有类的所有方法。

"@annotation(com.hyl.common.annotations.NeedLog)"   匹配@NeedLog注解所标注的方法。

学习遇到的问题:

struts2 会出现所有@Autowired全部失效,解决方案,在 struts.xml 中加上

<constant name="struts.objectFactory.spring.autoWire.alwaysRespect" value="true" /> 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值