OOP:Object Oriented Programming 面向对象编程
AOP: aspect oriented programming 面向切面编程
通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术
AOP:定义目标类----target 中的方法—切入点 pointcut
定义切面类----aspect 中的方法—通知 advice
把切面类中的通知切入到目标类中的切入点-----织入,实现织入使用动态代理
动态代理:
1. JDK动态代理—要求:必须有接口,基于父接口的动态代理
2:cglib实现动态代理,spring本身就支持cglib动态代理,不需要有接口,
代理对象是被代理类的子类对象
基于父类的动态代理
spring已经实现了aop:spring框架可以生成代理对象
1.定义目标类
2.定义切面类 AOP联盟,制定了切面类符合的接口规范
3.由spring生成代理对象,实现织入
AOP图:
具体实现的两种方法
1.JDK动态代理:基于接口的,必须存在父接口
Proxy.newProxyInstance(,,new InvocationHandler(){ })
(1)创建目标类
//目标类
public class UserServiceImpl implements UserService {
@Override//pointcut切入点
public void addUser() {
System.out.println("add user...");
}
@Override
public void deleteUser() {
System.out.println("delete user...");
}
@Override
public void updateUser() {
System.out.println("update user...");
}
}
(2)创建切面类
//切面类 aspect:在目标类的方法中要加入的功能
public class MyAspect {
public void before(){//advice
System.out.println("开启事务...");
}
public void after(){
System.out.println("提交事务...");
}
}
(3)JDK动态代理实现
public class Demo {
public static void main(String[] args) {
//创建目标类对象(被代理对象)
UserService userService = new UserServiceImpl();
//创建切面类对象
MyAspect myAspect = new MyAspect();
//利用JDK动态代理实现织入
UserService proxyObj =(UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myAspect.before();
Object obj = method.invoke(userService, args);
myAspect.after();
return obj;
}
});
proxyObj.addUser();
}
}
2.基于父类的,代理对象是被代理的类的子类对象, spring自带cglib的jar包
Enhancer en=new Enhancer();
en.setSuperClass(目标类.class);
en.setCallBack(new MethodInterceptor(){
});
en.create(); //返回代理对象
public class Demo {
public static void main(String[] args) {
//创建目标类对象
UserServiceImpl userService = new UserServiceImpl();
//创建切面类对象
MyAspect myAspect = new MyAspect();
//使用cglib生成代理对象
//创建一个增强类对象
Enhancer enhancer = new Enhancer();
//指定代理对象的类型
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
myAspect.before();
Object obj = method.invoke(userService, args);
myAspect.after();
return obj;
}
});
//生成代理对象
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
proxy.addUser();
proxy.deleteUser();
proxy.updateUser();
}
}
ps:coding过程中,发现Enhance不能自动导入,只能手动import相关的包,这一点比较奇怪,防止以后再次出现相应问题,我先备份一下:
import org.springframework.cglib.proxy.MethodProxy;
3.使用Aop联盟
//AOP联盟,制定了切面类的规范(接口)
public class MyAspect implements MethodInterceptor {
//环绕通知,在目标类中的切入点之前和之后切入功能
//前置通知,后置通知,异常抛出通知
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("开启事务...");
Object obj = methodInvocation.proceed();//执行目标类对象的方法
System.out.println("提交事务...");
return obj;
}
}
AOP联盟为通知Advice定义了
org.aopalliance.aop.Advice
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
• 前置通知 :在目标方法执行前实施增强
org.springframework.aop.MethodBeforeAdvice
• 后置通知:在目标方法执行后实施增强
org.springframework.aop.AfterReturningAdvice
• 环绕通知:在目标方法执行前后实施增强
org.aopalliance.intercept.MethodInterceptor
• 异常抛出通知 :在方法抛出异常后实施增强
org.springframework.aop.ThrowsAdvice //在catch中切入代码
• 引介通知 :在目标类中添加一些新的方法和属性
org.springframework.aop.IntroductionInterceptor
spring实现aop的两种方式:
1.半自动方式:
<bean id = "" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--指定代理对象实现的接口-->
<property name="interfaces" value="com.aop3.UserService"></property>
<!--指定目标类对象的名字-->
<property name="targetName" value="userService"></property>
<!--指定切面类对象-->
<property name="interceptorNames" value="aspectId"></property>
</bean>
2.全自动方式:
(1) 切面类符合aop联盟规范
<aop:config>
<aop:pointcutid="points"expression="execution(*com.aop4.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="aspectId" pointcut-ref="points"></aop:advisor>
</aop:config>
(2)切面类符合AspectJ规范
<aop:config>
<aop:aspect ref="aspectId">
<!--使用该标签说明myBefore方法属于前置通知-->
<!--<aop:before method="myBefore" pointcut="execution(* com.aop5.UserServiceImpl.*(..))"></aop:before>-->
<!--使用该标签说明myAfter方法属于前置通知-->
<!--<aop:after-returning method="myAfter" pointcut="execution(* com.aop5.UserServiceImpl.*(..))" returning="ret"></aop:after-returning>-->
<aop:pointcut id="points" expression="execution(* com.aop5.UserServiceImpl.*(..))"/>
<!--环绕通知-->
<aop:around method="myAround" pointcut-ref="points"></aop:around>
<!--异常通知-->
<aop:after-throwing method="myException" pointcut-ref="points" throwing="t"></aop:after-throwing>
<!--最终通知-->
<aop:after method="myFinal" pointcut-ref="points"></aop:after>
</aop:aspect>
</aop:config>
切入点表达式:
语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符:一般省略
public: 公共方法
星号 : 任意
返回值:不能省略
void :返回没有值
String: 返回值字符串
星号 : 任意
包:[省略]
com.dzc.com 固定包
com.dzc.com.kk.service
com.dzc.com.“星号”.service com包下面子包任意 (例如:com.dzc.com.staff.aa.bb…service)
com.dzc.com… com包下面的所有子包(含自己)com.dzc.com.staff.cc.kk
com.dzc.com.*.service… com包下面任意子包,固定目录service,service目录任意包
类,[省略]
UserServiceImp: 指定类
"星号"Impl : 以Impl结尾
User"星号": 以User开头
"星号": 任意
方法名,不能省略
addUser:固定方法
add"星号":以add开头
"星号"Do:以Do结尾
"星号":任意
(参数)
(): 无参
(int): 一个整型
(int ,int: 两个
(…): 参数任意
throws ,可省略,一般不写。
利用spring配置.xml文件的方法定义AspectJ风格的切面类
1.切面类
//定义AspectJ风格的切面类
public class MyAspect {
//自定义前置通知
//前置通知
public void myBefore(){
System.out.println("事务执行前");
}
//后置通知 可以获取切入点方法的返回值
public void myAfter(Object ret){
System.out.println("事务执行后,返回值是"+ret);
}
//环绕通知:返回值类型必须是Object,参数必须是ProceddingJoinPoint类型 切入点发生异常执行
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("开启事务..");
Object obj = joinPoint.proceed();//执行的是目标类对象的方法
System.out.println("关闭事务..");
return obj;
}
//异常通知
public void myException(Throwable t){
System.out.println("出大问题了老爷!"+t.getMessage());
}
//最终通知
public void myFinal(){
System.out.println("最终通知..");
}
}
2.bean.xml配置
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 创建目标类对象:被代理的对象-->
<bean id="userService" class="com.aop5.UserServiceImpl"></bean>
<!-- 创建切面类对象-->
<bean id="aspectId" class="com.aop5.MyAspect"></bean>
<aop:config>
<aop:aspect ref="aspectId">
<!--使用该标签说明myBefore方法属于前置通知-->
<!--<aop:before method="myBefore" pointcut="execution(* com.aop5.UserServiceImpl.*(..))"></aop:before>-->
<!--使用该标签说明myAfter方法属于前置通知-->
<!--<aop:after-returning method="myAfter" pointcut="execution(* com.aop5.UserServiceImpl.*(..))" returning="ret"></aop:after-returning>-->
<aop:pointcut id="points" expression="execution(* com.aop5.UserServiceImpl.*(..))"/>
<!--环绕通知-->
<aop:around method="myAround" pointcut-ref="points"></aop:around>
<!--异常通知-->
<aop:after-throwing method="myException" pointcut-ref="points" throwing="t"></aop:after-throwing>
<!--最终通知-->
<aop:after method="myFinal" pointcut-ref="points"></aop:after>
</aop:aspect>
</aop:config>
</beans>
利用注释的方法定义AspectJ风格的切面类
//定义AspectJ风格的切面类
@Component
//指定当前类是切面类
@Aspect
public class MyAspect {
//前置通知
@Before("execution(* com.aop6.UserServiceImpl.*(..))")
public void myBefore(){
System.out.println("事务执行前");
}
@Pointcut("execution(* com.aop6.UserServiceImpl.*(..))")
public void myPoint(){}
//后置通知 可以获取切入点方法的返回值
@AfterReturning(value = "myPoint()",returning = "ret")
public void myAfter(Object ret){
System.out.println("事务执行后,返回值是"+ret);
}
//环绕通知:返回值类型必须是Object,参数必须是ProceddingJoinPoint类型 切入点发生异常执行
@Around("myPoint()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("开启事务..");
Object obj = joinPoint.proceed();//执行的是目标类对象的方法
System.out.println("关闭事务..");
return obj;
}
//异常通知
@AfterThrowing(value = "myPoint()",throwing = "t")
public void myException(Throwable t){
System.out.println("出大问题了老爷!"+t.getMessage());
}
//最终通知
@After("myPoint()")
public void myFinal(){
System.out.println("最终通知..");
}
}