[Spring]AOP切面程式設計/原理/基於註解/基於xml
阿新 • • 發佈:2019-02-18
AOP概念
不講廢話,面向切面就比如說有很多個業務邏輯程式碼,如果你要修改程式碼,在程式碼實現前後增加一條邏輯,比如要判斷後才執行程式碼,你總不能一條條去改各個類的程式碼。
所以切面就是說執行一個方法,這個方法變為一個切入點來配置,你可以定義這個切入點,你想在執行這個方法之前增加邏輯或者在它之後增加邏輯,都可自行配置。
原理(Proxy&CGlib)
Proxy實現(JDK的AOP,不是框架裡的)
定義一個代理工廠攔截了業務bean,先判斷。
package com.yiki.service; public interface PersonSercvice { public void save(String name); public void update(String name,Integer pid); public String getPname(Integer pid); }
package com.yiki.bean; import com.yiki.service.PersonSercvice; public class Person implements PersonSercvice{ private String user = null; public Person() { } public Person(String user) { this.user=user; } public String getUser() { return user; } @Override public void save(String name) { System.out.println("save'"); } @Override public void update(String name, Integer pid) { System.out.println("update"); } @Override public String getPname(Integer pid) { // TODO Auto-generated method stub return "getname"; } }
package com.yiki.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.aopalliance.intercept.Invocation; import com.yiki.bean.Person; public class JDKProxyFactory implements InvocationHandler { private Object target; public Object createProxyInstance(Object target) { this.target = target; Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this); return target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Person bean = (Person) this.target; Object result = null; if (bean.getUser() != null) { result = method.invoke(target, args); } return result; } }
package com.yiki.test;
import org.junit.Test;
import com.yiki.aop.JDKProxyFactory;
import com.yiki.bean.Person;
import com.yiki.service.PersonSercvice;
public class aoptest {
@Test
public void testProxy(){
JDKProxyFactory proxy = new JDKProxyFactory();
PersonSercvice service = (PersonSercvice) proxy.createProxyInstance(new Person("yiki"));
service.save("888");
//PersonSercvice service = (PersonSercvice) proxy.createProxyInstance(new Person());沒有輸出
}
}
Spring cglib實現
package com.yiki.aop;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.yiki.bean.People;
import com.yiki.bean.Person;
public class cglibFactory implements MethodInterceptor {
private Object target;
public Object createProxyInstance(Object target) {
this.target=target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy methodproxy) throws Throwable {
People bean = (People) this.target;
Object result = null;
if (bean.getUser() != null) {
result = methodproxy.invoke(target, args);
}
return result;
}
}
package com.yiki.bean;
public class People {//不需要實現介面
private String user;
public People() {
}
public People(String user) {
this.user = user;
}
public String getUser() {
return user;
}
public void save(String name) {
System.out.println("save'");
}
}
@Test
public void testcglib(){
cglibFactory cglib = new cglibFactory();
People people = (People) cglib.createProxyInstance(new People("yiki"));//為空則不輸出
people.save("000");
}
框架實現
Spring AOP註解實現(真正的Spring框架實現)
這裡只涉及最普通的註解,具體切面的匹配(就是@PointCut(匹配規則))以及一些需要傳入引數(&&args)的情況要另算……(太多了不知道怎麼寫- -。)
首先這裡要先把配置檔案自動掃描給加上。
<?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-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<context:annotation-config />
<!-- 自動掃描所有的bean -->
<context:component-scan base-package="com.yiki.bean" />
<!-- 宣告自動為spring容器中那些配置@aspectJ切面的bean建立代理,織入切面 -->
<aop:aspectj-autoproxy />
</beans>
這裡我已經不會在配置檔案寫bean了,而是用@Compenent
package com.yiki.bean;
import org.springframework.stereotype.Component;
@Component
public class Student {
public void student(){
System.out.println("student");
}
}
切面類
package com.yiki.bean;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class aspect {
@Pointcut("execution (* com.yiki.bean.Student.*(..))")//匹配Student下的所有方法
private void anyMethod() {
}// 空方法,是一個切入點
@Before("anyMethod()")
public void before() {
System.out.println("before前置通知");
}
@AfterReturning("anyMethod()")
public void after() {
System.out.println("afterReturning後置通知");
}
@After("anyMethod()")
public void doAfter(){
System.out.println("after最終通知");
}
@Around("anyMethod()")//適合許可權控制,萬能~~~~~
public Object around(ProceedingJoinPoint pjp) throws Throwable{
Object result = pjp.proceed();
try {
//=AfterReturning
} catch (Exception e) {
// =before
}finally {
//後置通知
}
System.out.println("around環繞通知");
return result;//最終
}
}
測試
package com.yiki.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.yiki.bean.Student;
public class tests {
public static void main(String[] args) {
String xmlPath = "ApplicationContext.xml";
ApplicationContext cfg = new ClassPathXmlApplicationContext(xmlPath);
Student stu = (Student) cfg.getBean("student");
stu.student();
}
}
基於xml配置檔案的AOP(只支援單例模式)
遵循以下原則:
<bean id="student" class="com.yiki.bean.Student"></bean>
<bean id="aspect" class="com.yiki.bean.aspect"></bean>
<!-- 把student作為一個切面宣告,這個切面的名字是aspectAOP
【expression】裡寫需要匹配的方法,*後面有空格
-->
<aop:config>
<aop:aspect id="aspectAOP" ref="aspect">
<aop:pointcut id="doOne" expression="execution(* com.yiki.bean.Student.*(..))" />
</aop:aspect>
</aop:config>
對於切入點PointCut:execution(這裡自己查官方吧,很多匹配規則)
<?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
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- bean definition & AOP specific configuration -->
<bean id="student" class="com.yiki.bean.Student"></bean>
<bean id="aspect" class="com.yiki.bean.aspect"></bean>
<!-- 把aspect作為一個切面宣告,這個切面的名字是aspectAOP
切入點是student裡的所有方法,==》【expression】裡寫需要匹配的方法,*後面有空格
-->
<aop:config>
<aop:aspect id="aspectAOP" ref="aspect">
<aop:pointcut id="doOne" expression="execution(* com.yiki.bean.Student.*(..))" />
<!-- advice通知配置 -->
<aop:before method="before" pointcut-ref="doOne"/>
<aop:after-returning method="after" pointcut-ref="doOne"/>
<aop:around method="around" pointcut-ref="doOne"/>
</aop:aspect>
</aop:config>
</beans>
package com.yiki.bean;
import org.aspectj.lang.ProceedingJoinPoint;
public class aspect {
public void show(){
System.out.println("aspect");
}
public void before() {
System.out.println("before前置通知");
}
public void after() {
System.out.println("afterReturning後置通知");
}
public void doAfter(){
System.out.println("after最終通知");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable{
Object result = pjp.proceed();
try {
//=AfterReturning
System.out.println("之後Runing環繞通知");
} catch (Exception e) {
// =before
System.out.println("之前環繞通知");
}finally {
//後置通知
System.out.println("之後環繞通知");
}
System.out.println("around環繞通知");
return result;//最終
}
}