归结到底就是通过反射实现AOP,实现 InvocationHandler 类的 public Object invoke(Object proxy, Method method, Object[] args) {;}方法;
通过IOC模式将AOP配置到xml
Spring中AOP是怎么实现的呢?
Spring中AOP的有两种实现方式:
1、JDK动态代理
2、Cglib动态代理
Spring实现面向切面的逻辑:
切面(Aspect):其实就是共有功能的实现。如日志切面、权限切面、事务切面等。在实际应用中通常是一个存放共有功能实现的普通Java类,之所以能被AOP容器识别成切面,是在配置中指定的。
通知(Advice):是切面的具体实现。以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)5种。在实际应用中通常是切面类中的一个方法,具体属于哪类通知,同样是在配置中指定的。
连接点(Joinpoint):就是程序在运行过程中能够插入切面的地点。例如,方法调用、异常抛出或字段修改等,但Spring只支持方法级的连接点。
切入点(Pointcut):用于定义通知应该切入到哪些连接点上。不同的通知通常需要切入到不同的连接点上,这种精准的匹配是由切入点的正则表达式来定义的。
目标对象(Target):就是那些即将切入切面的对象,也就是那些被通知的对象。这些对象中已经只剩下干干净净的核心业务逻辑代码了,所有的共有功能代码等待AOP容器的切入。
代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象的核心业务逻辑功能加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。
织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期,当然不同的发生点有着不同的前提条件。譬如发生在编译期的话,就要求有一个支持这种AOP实现的特殊编译器;发生在类装载期,就要求有一个支持AOP实现的特殊类装载器;只有发生在运行期,则可直接通过Java语言的反射机制与动态代理机制来动态实现。
代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。
一个例子:
------------------------------ ------------------------------
cmd
进入E 盘
E:
进入相关文件目录
cd /src/main/java/com/springAOP
tree /f
tree /f F:\...\src\main\java\com\springAOP
------------------------------------------com.springAOP包下文件结构
│ UserServiceTest.java
├─aop
│ LogInterceptor.java
├─dao
│ │ UserDAO.java
│ └─impl
│ UserDAOImpl.java
├─model
│ User.java
└─service
UserService.java
UserServiceTest.java
package com.springAOP;
import com.springAOP.aop.LogInterceptor;
import com.springAOP.dao.UserDAO;
import com.springAOP.dao.impl.UserDAOImpl;
import com.springAOP.model.User;
import com.springAOP.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.reflect.Proxy;
/**
* Created by Y on 2019/11/7.
*/
public class UserServiceTest {
@Test
public void testProxy() throws Exception{//代理测试
UserDAO userDAO = new UserDAOImpl();
LogInterceptor logInterceptor= new LogInterceptor();//NEW一个代理对象
logInterceptor.setTarget(userDAO); //把需要代理的对象交给代理对象处理
//调用Proxy类的静态方法 newProxyInstance
//返回值是代理对象,该代理对象实现了被代理对象的方法
UserDAO userDAOProxy=(UserDAO)Proxy.newProxyInstance(userDAO.getClass().getClassLoader(),new Class[]{UserDAO.class},logInterceptor);
UserDAO userDAOProxy2=(UserDAO)Proxy.newProxyInstance(userDAO.getClass().getClassLoader(),userDAO.getClass().getInterfaces(),logInterceptor);
//上面 userDAOProxy、userDAOProxy2产生的相同结果,产生被代理
System.out.println(userDAOProxy.getClass());
System.out.println(userDAOProxy.getClass().getName());
System.out.println(userDAOProxy.getClass().getInterfaces());
System.out.println(userDAOProxy.getClass().getInterfaces().getClass().getName());
System.out.println(userDAOProxy.getClass().getInterfaces().getClass().getInterfaces());
System.out.println(userDAOProxy.getClass().getClassLoader());
userDAOProxy.delete(new User());//返回值是代理对象,该代理对象实现了被代理对象的方法 所以可以直接调用被代理类 的方法
userDAOProxy2.save(new User());
}
}
LogInterceptor.java
package com.springAOP.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Created by Y on 2019/11/7.
* 代理
*/
public class LogInterceptor implements InvocationHandler {
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public void beforeMethod(Method method){
//System.out.println("save start...");
System.out.println(method.getName() +" start...代理被调用");
}
public void afterMethod(Method method){
//System.out.println("save start...");
System.out.println(method.getName() +" end...代理被调用");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod(method);
System.out.println(this.getClass().getName()+"================================== target:" +target+" args:"+args);
method.invoke(target,args);
afterMethod(method);
return null;
}
}
UserDAO.java
package com.springAOP.dao;
import com.springAOP.model.User;
/**
* Created by Y on 2019/11/7.
*/
public interface UserDAO {
public void save(User user);
public void delete(User user);
}
UserDAOImpl.java
package com.springAOP.dao.impl;
import com.springAOP.aop.LogInterceptor;
import com.springAOP.dao.UserDAO;
import com.springAOP.model.User;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
/**
* Created by Y on 2019/11/7.
*/
//@Component(value = "userDAO") //userDAOImpl 组件 可以看为另一个类的资源 在UserService注解key @Resource(name = "userDAO")
//@Component("userDAO")
//@Component(value = "userDAO")
@Repository(value = "userDAO")
public class UserDAOImpl implements UserDAO {
@Override
public void save(User user) {
//Hibernate
//JDBC
//XML
//NetWork
// new LogInterceptor().beforeMethod(null);
//userDAO.save(user);
System.out.println(this.getClass().getName()+"-----: user saved!");
}
@Override
public void delete(User user) {
System.out.println(this.getClass().getName()+"-----: user deleted!");
}
}
User.java
package com.springAOP.model;
/**
* Created by Y on 2019/11/7.
*/
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
/*
Run...
"D:\Program Files\jdk1.6.0_43\bin\java"........;E:\M2\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 com.springAOP.UserServiceTest,testProxy
class com.sun.proxy.$Proxy5
com.sun.proxy.$Proxy5
[Ljava.lang.Class;@9664a1
[Ljava.lang.Class;
[Ljava.lang.Class;@1a8c4e7
sun.misc.Launcher$AppClassLoader@1a46e30
delete start...代理被调用
com.springAOP.aop.LogInterceptor================================== target:com.springAOP.dao.impl.UserDAOImpl@1172e08 args:[Ljava.lang.Object;@cf2c80
com.springAOP.dao.impl.UserDAOImpl-----: user deleted!
delete end...代理被调用
save start...代理被调用
com.springAOP.aop.LogInterceptor================================== target:com.springAOP.dao.impl.UserDAOImpl@1172e08 args:[Ljava.lang.Object;@1729854
com.springAOP.dao.impl.UserDAOImpl-----: user saved!
save end...代理被调用
Process finished with exit code 0
*/
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓Spring 注入切面,但一般都会在xml中配置↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
package com.springAOP.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.Before;
/**
* Created by Y on 2019/11/7.
*
* Spring 自动代理
*/
//@Aspect //切面
@Component //必须要初始化才能使用
@Aspect
public class LogInterceptorAspect implements InvocationHandler {
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
//@Pointcut("execution(public * com.itsoft.action..*.*(..))")
@Pointcut("execution(public * com.springAOP.dao.impl.UserDAOImpl.save(com.springAOP.model.User))")
public void recordLog(){}
//方法上面的切面逻辑(可以是属性 方法等)
@Before("execution(public void com.springAOP.dao.impl.UserDAOImpl.save(com.springAOP.model.User))") //该注解导致异常
//@Before("execution(* com.springAOP.dao.impl.UserDAOImpl.save(..))")
//@Before("recordLog()")
public void beforeMethod(Method method){
//System.out.println("save start...");
System.out.println(method.getName() +" start...代理被调用Aspect");
}
@After("recordLog()")
public void afterMethod(Method method){
//System.out.println("save start...");
System.out.println(method.getName() +" end...代理被调用Aspect");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod(method);
System.out.println(this.getClass().getName()+"================================== target:" +target+" args:"+args);
method.invoke(target,args);
afterMethod(method);
return method;
}
}