首先我们看看下面的思路
平常加日志的代码
- 日志不属于业务逻辑,却与业务混合在一起,业务组件添加了额外的日志责任
- 大量组件需要日志,哪一天不需要这样的日志,修改很大
解决方案1:使用代理
这样会导致代理类太多
解决方法2:动态代理
public class DynamicProxy{
private Logger logger = Logger.getLogger(this.getClass().getName());
private Object delegate;
public Object bind(Object delegate) {
this.delegate = delegate;
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
delegate.getClass().getInterfaces(),this::invoke);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
logger.log(Level.INFO, "method starts " + method);
result = method.invoke(delegate, args);
logger.log(Level.INFO, "method ends " + method);
return result;
}
}
public class test {
public static void main(String[] args) {
DynamicProxy dynamicProxy= new DynamicProxy();
IStudentService service = (IStudentService ) dynamicProxy.bind( new StudentService() );
service.addStudent(new Student());
}
}
什么是AOP
将系统中与业务无关的动作或服务(如:日志记录、事务处理、权限控制)设计为通用、不介入特定业务对象的、职责清楚的Aspect对象,这就是Aspect-oriented programming,即AOP编程
一丶几个概念
- 本例中StudentService本身的职责添加学生,却必须插入日志记录这样的与业务无关的动作,这样的动作AOP中称为横切关切点(Cross-cutting-concern)
- 使用代理对象将记录等与业务无关的动作或任务提出来,设计为一个服务对象,这样的对象称为切面(Aspect)
- Advice(通知)
Aspect指的是一类服务,而Advice是这类服务当中具体的一个,如日志服务前面的DynamicProxy类(Advice与Aspect是具体与抽象的关系) - Joinpoint(织入时机,连接点)
在业务流程执行时织入Aspect对象的时机,如业务方法执行之前、之后、异常发生时 - Pointcut(切入点)
Pointcut是一个定义,可以由它定义一些织入时机(Joinpoint) - Target
一个Advice被应用的对象或目标,如前面StudentService就是Advice DynamicProxy的target
二丶Spring AOP的写法
三个业务类
public class Student {
}
public interface IStudentService {
public void addStudent(Student student);
}
public class StudentService implements IStudentService{
public void addStudent(Student student){
System.out.println("学生已添加");
}
}
两个Advice类
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LogBeforeAdvice implements MethodBeforeAdvice {
private Logger logger = Logger.getLogger(this.getClass().getName());
public void before(Method method, Object[] args, Object target) {
logger.log(Level.INFO, "method starts "+ method);
}
}
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LogAfterAdvice implements AfterReturningAdvice {
private Logger logger = Logger.getLogger(this.getClass().getName());
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) {
logger.log(Level.INFO, "method ends "+ method);
}
}
编织
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans>
<bean id="LogBeforeAdvice"
class="LogBeforeAdvice"/>
<bean id="LogAfterAdvice"
class="LogAfterAdvice"/>
<bean id="StudentService"
class="StudentService"/>
<bean id="StudentServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>IStudentService</value>
</property>
<property name="target">
<ref bean="StudentService"/>
</property>
<property name="interceptorNames">
<list>
<value>LogAfterAdvice</value>
<value>LogBeforeAdvice</value>
</list>
</property>
</bean>
</beans>
</beans>
测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class test {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("src/applicationContext.xml");
IStudentService StudentService =
(IStudentService) context.getBean("StudentServiceProxy");
StudentService.addStudent(new Student());
}
三丶实战
- 使用AspectJ注解,毕竟太麻烦了写配置文件
- 结合Spring IOC和Spring MVC
三个业务类
public class Student {
}
public interface IStudentService {
public void addStudent(Student student);
}
@Component
public class StudentService implements IStudentService{
public void addStudent(Student student){
System.out.println("学生已添加");
}
}
一个Advice类
@Aspect
@Component
public class LogAdvice{
private Logger logger = Logger.getLogger(this.getClass().getName());
@Before("execution(* StudentService.addStudent(..))")
public void before() {
logger.log(Level.INFO, "method starts ");
}
@After("execution(* StudentService.addStudent(..))")
public void afterReturning() {
logger.log(Level.INFO, "method ends ");
}
}
一个Controller类
@Controller
@RequestMapping("/o")
public class TestController {
@Autowired
private IStudentService studentService;
@RequestMapping("/ok")
public String run() {
studentService.addStudent(new Student());
return "index";
}
}
编制
我们使用注解帮我们编制,那么我们只需要扫描
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="test"/>
<aop:aspectj-autoproxy />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
改一下web.xml下面部分
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
接下来运行网页,输入网址
http://localhost:8080/untitled7_war_exploded/o/ok
结果如下