SpringAOP

1.名词认识

目标对象(TargetObject): 需要实现代理的目标对象
连接点(Joinpoint): 连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
切入点(Pointcut): 切入点是指我们要对哪些Joinpoint进行拦截定义(方法增强)
通知(Advice): 通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
织入(Weaver): 是指把增强方法应用到目标对象过程
介引(Introduction): 动态的为某个类增加或减少方法
切面(Aspect): 是切入点和通知(引介)的结合
代理(Proxy): 一个类被AOP织入增强后,就产生一个结果代理类

2.jdk实现代理

  1. 需要使用java.lang.reflect.Proxy代理类
  2. 需要代理的类必须实现接口,且只能代理接口中的方法
  • 准备需要代理的类,并实现UserService接口
package service.impl;

import entity.User;
import service.UserService;

public class UserServiceImpl implements UserService{
	@Override
	public boolean addUser() {
	    System.out.println("往数据库里添加了一个user");
		return true;		
	}
}
  • 准备代理通知类
package service.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import service.UserService;

public class UserAdvice implements InvocationHandler{
    //需要代理的对象
	private UserService us;
	
	public UserAdvice(UserService us) {
		super();
		this.us = us;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//方法执行前需要执行的操作
		System.out.println("----------正在执行-----------");
		//需要代理执行的方法
		Object obj=method.invoke(us, args);
		//方法执行后需要执行的操作
		System.out.println("----------执行完毕-----------");
		//代理方法的返回值,无返回值则为null
		return obj;
	}
}
  • 测试
@org.junit.Test
	public void load4(){
	    UserServiceImpl us1=new UserServiceImpl();
		//代理关键步骤
		UserService us2=(UserService)Proxy.newProxyInstance(us1.getClass().getClassLoader(), us1.getClass().getInterfaces(),new UserAdvice(us1));
	    us2.addUser(new User("张三", 20));
	}
  • 结果
----------正在执行-----------
往数据库里添加了一个user
----------执行完毕-----------

3.AOP实现方法增强(织入、代理)

  1. 添加jar包
aspectjweaver-1.6.9.jar
aopalliance-1.0.jar
org.springframework.aop-3.1.1.RELEASE.jar
  1. 需要增强的类
package service.impl;

import dao.UserDao;
import entity.User;
import service.UserService;

public class UserServiceImpl implements UserService{
    private UserDao ud;
	
	public void setUd(UserDao ud) {
		this.ud = ud;
	}

	@Override
	public boolean addUser(User user) {
		//int a=8/0;
		return ud.insert(user);		
	}
}
  1. 关于切点的正则表达
execution(public * *(..))  :匹配任何公用方法

execution(* set*(..)) :匹配名称以“set”开头的方法

execution(* com.xyz.service.AccountService.*(..)) :匹配AccountService接口定义的任何方法

execution(* com.xyz.service.*.*(..)) :匹配指定包中所有类型中的所有方法

execution(* com.xyz.service..*.*(..)) :匹配指定包中以及子包中所有类型的所有方法

within(com.xyz.service.*) :匹配指定包中的所有类

within(com.xyz.service..*) :匹配指定包中及子包中的所有类

this(com.xyz.service.AccountService) :匹配实现AccountService接口的任何连接点

target(com.xyz.service.AccountService) :匹配目标对象实现AccountService接口的任何连接点

args(java.io.Serializable) :匹配任何接受单个参数的连接点,并且参数类型是java.io.Serializable类型
方式一:通过接口
MethodBeforeAdvice   //前置增强(方法执行前执行)
AfterReturningAdvice //后置增强(方法执行后执行)
ThrowsAdvice         //异常增强(方法中出现异常执行)
MethodInterceptor    //环绕增强(对方法具有绝对控制权)
  • 实现接口(增强类)
package aop;

import java.lang.reflect.Method;
import java.util.Arrays;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

public class UserServiceLogger2 implements MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice,MethodInterceptor{

	private static final Logger logger=Logger.getLogger(UserServiceLogger1.class);
	
	@Override
	//目标方法执行前(前置通知)
	public void before(Method method, Object[] args, Object target)
			throws Throwable {
		logger.error("前置通知:目标类型"+target.getClass().getName()+"--目标方法:"+method.getName()+"--参数:"+Arrays.toString(args));
	}
	
	@Override
	//目标方法执行后(后置通知)
	public void afterReturning(Object returnValue, Method arg1, Object[] args,
			Object target) throws Throwable {
		logger.error("后置通知:参数:"+Arrays.toString(args)+"--返回值:"+returnValue.toString());
	}
	
	//目标发生异常时(异常通知)
	public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
		logger.error("异常通知:参数:"+Arrays.toString(args)+"--异常:"+ex.getMessage());
	}

	@Override
	//拦截对目标方法的调用,即调用目标方法的整个过程(环绕通知)
	public Object invoke(MethodInvocation inv) throws Throwable {
		Object result=null;
		try {
			//前置通知
			logger.error("【环绕前置通知】--参数个数:"+inv.getArguments().length);
			result=inv.proceed();  //方法执行(切入点的方法执行)
			//后置通知
			logger.error("【环绕后置通知】--返回值:"+result.toString());
		} catch (Throwable e) {
			logger.error("【环绕异常通知】--异常信息:"+e.getMessage());
		}finally{
			logger.error("【环绕最终通知】");
		}
		return result;
	}
}
  • 配置文件添加如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <!-- 声明dao的bean -->
	<bean id="ud" class="dao.impl.UserDaoImpl"></bean>

    <!--需要增强类的bean对象-->
	<bean id="us" class="service.impl.UserServiceImpl">
		<property name="ud" ref="ud" />
	</bean>
	
	<!--增强类的bean对象-->
	<bean id="usl" class="aop.UserServiceLogger2"></bean>

    <!--增强核心配置-->
    <aop:config>
      <!--配置切点-->
      <aop:pointcut expression="execution(* service..*UserServiceImpl.addUser(..))" id="pointcut"/>
      <!--配置引用增强-->
      <aop:advisor advice-ref="usl" pointcut-ref="pointcut"/>
    </aop:config>
</beans>
方式二:通过配置文件实现
  • 增强类
package aop;

import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class UserServiceLogger1 {

	private static final Logger logger=Logger.getLogger(UserServiceLogger1.class);
	
	//前置通知 beforeAdvice
	public void before(JoinPoint j){
		logger.error("前置通知:目标类型"+j.getTarget().getClass().getName()+"--目标方法:"+j.getSignature().getName()+"--参数:"+Arrays.toString(j.getArgs()));
	}
	
	//后置通知 afterRetueningAdvice
	public void afterReturning(JoinPoint j,Object result){
		logger.error("后置通知:参数:"+Arrays.toString(j.getArgs())+"--返回值:"+result.toString());
	}
	
	//异常通知 afterThrowingAdvice
	public void afterThrowing(JoinPoint j,Exception e){
		logger.error("异常通知:参数:"+Arrays.toString(j.getArgs())+"--异常:"+e.getMessage());
	}
	
	//最终通知 afterAdvice
	public void after(JoinPoint j){
		logger.error("最终通知,无论有没有异常都会执行");
	}
	
	//环绕通知 aroundAdvice(拥有绝对控制权)
	public Object around(ProceedingJoinPoint j){
		Object result=null;
		try {
			//前置通知
			logger.error("【环绕前置通知】--参数个数:"+j.getArgs().length);
			result=j.proceed();  //方法执行(切入点的方法执行)
			//后置通知
			logger.error("【环绕后置通知】--返回值:"+result.toString());
		} catch (Throwable e) {
			logger.error("【环绕异常通知】--异常信息:"+e.getMessage());
		}finally{
			logger.error("【环绕最终通知】");
		}
		return result;
	}
}
  • 配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <!-- 声明dao的bean -->
	<bean id="ud" class="dao.impl.UserDaoImpl"></bean>

    <!--需要增强类的bean对象-->
	<bean id="us" class="service.impl.UserServiceImpl">
		<property name="ud" ref="ud" />
	</bean>
	
	<!--增强类的bean对象-->
	<bean id="usl" class="aop.UserServiceLogger1"></bean>
	
	<aop:config>
	   <!-- 配置切入点 -->
	   <aop:pointcut expression="execution(* service..*.*addUser(..))" id="pointcut"/>
	   <!-- 配置引用增强bean对象 -->
	   <aop:aspect ref="usl">
	      <!--环绕增强-->
	      <aop:around method="around" pointcut-ref="pointcut" />
	      <!--前置增强-->
	      <aop:before method="before" pointcut-ref="pointcut"/>
	      <!--后置增强 returning返回值属性:值必须与增强方法中的形参一致-->
	      <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result" />
	      <!--异常增强 throwing异常属性:值必须与增强方法中的形参一致-->
	      <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e" />
	      <!--最终增强-->
	      <aop:after method="after" pointcut-ref="pointcut" />
	   </aop:aspect>
	</aop:config>
</beans>
方式三:通过注解实现
  • 注解类型
@Aspect //定义切面的注解
@Before //定义前置增强方法的注解
@AfterReturning  //定义后置增强方法的注解
@AfterThrowing  //定义异常增强方法的注解
@After  //定义最终增强方法的注解
@Around  //定义环绕增强方法的注解
  • 配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <!-- 开启自动织入配置 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
    <!-- 配置自动扫描 -->
    <context:component-scan base-package="dao.impl,service.impl,aop"></context:component-scan>
</beans>
  • 需要被增强织入的类
package service.impl;

import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import dao.UserDao;
import entity.User;
import service.UserService;

@Service("us")  //添加自动注入注解
public class UserServiceImpl implements UserService{
	@Resource(name="ud") //注入指定对象
    private UserDao ud;
	
	public void setUd(UserDao ud) {
		this.ud = ud;
	}

	@Override
	public boolean addUser(User user) {
		//int a=8/0;
		return ud.insert(user);		
	}
}
  • 增强类
package aop;

import java.util.Arrays;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;


@Component("ul")
@Aspect  //定义此类为切面的注解
public class UserServiceLogger3 {

	private static final Logger logger=Logger.getLogger(UserServiceLogger3.class);
	
	//前置通知 beforeAdvice
	@Before("within(service..*)")  //前置增强注解
	public void before(JoinPoint j){
		logger.error("前置通知:目标类型"+j.getTarget().getClass().getName()+"--目标方法:"+j.getSignature().getName()+"--参数:"+Arrays.toString(j.getArgs()));
	}
	
	//后置通知 afterReturningAdvice
	//后置增强注解
	@AfterReturning(pointcut="execution(* service..*UserServiceImpl.addUser(..))",returning="result")
	public void afterReturning(JoinPoint j,Object result){
		logger.error("后置通知:参数:"+Arrays.toString(j.getArgs())+"--返回值:"+result.toString());
	}
	
	//异常通知 afterThrowingAdvice
	//异常通知注解
	@AfterThrowing(pointcut="execution(* service..*UserServiceImpl.addUser(..))",throwing="e")
	public void afterThrowing(JoinPoint j,Exception e){
		logger.error("异常通知:参数:"+Arrays.toString(j.getArgs())+"--异常:"+e.getMessage());
	}
	
	//最终通知 afterAdvice
	//最终通知注解
	@After("execution(* service..*UserServiceImpl.addUser(..))")
	public void after(JoinPoint j){
		logger.error("最终通知");
	}
	
	//环绕通知 aroundAdvice(拥有绝对控制权)
	//环绕通知注解
	@Around("execution(* service..*UserServiceImpl.addUser(..))")
	public Object around(ProceedingJoinPoint j){
		Object result=null;
		try {
			//前置通知
			logger.error("【环绕前置通知】--参数个数:"+j.getArgs().length);
			result=j.proceed();  //方法执行(切入点的方法执行)
			//后置通知
			logger.error("【环绕后置通知】--返回值:"+result.toString());
		} catch (Throwable e) {
			logger.error("【环绕异常通知】--异常信息:"+e.getMessage());
		}finally{
			logger.error("【环绕最终通知】");
		}
		return result;
	}
}

4.SpringAOP代理模式认识

4.1.三种情况
  1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
  2. 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
  3. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
4.2.如何强制使用CGLIB实现AOP?
  1. 强制使用CGLIB代理设置proxy-target-class属性值为"true"
    <aop:config proxy-target-class="true"> </aop:config>
  2. 需要使用CGLIB代理和@AspectJ自动代理支持
    <aop:aspectj-autoproxy proxy-target-class="true" />
4.3.JDK动态代理和CGLIB字节码生成的区别?
  1. JDK动态代理只能对实现了接口的类生成代理,而不能针对类
  2. CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值