手寫實現Spring AOP
Spring IOC、AOP可謂是spring中兩大王牌
上次我們手寫了IOC,那麼今天我們來手寫一下AOP
我是學習他的寫法,然後將一些我不懂的程式碼進行了註釋解釋,應該可以更加適合小白學習吧。
一.準備
首先我們都知道Spring AOP的核心技術是動態代理,但是在這個demo中用的不是Jdj1.8的動態代理方法。
用的是cgib的動態代理方式。
那麼這二者有什麼區別呢?
JDK動態代理: 只能代理實現了介面的類
沒有實現介面的類不能實現JDK動態代理。
Cglib代理: 針對類來實現代理,對指定目標
產生一個子類 通過方法攔截技術攔截所有父類方法的呼叫。
二.思路
1 掃描 aop 包, 獲取 aspect 的類
2 根據 切點 獲取該切點的 類 和 方法
3 根據配置的 類 和 方法 為該類生成一個代理物件
4 將改代理物件放入 bean Map 中
5 呼叫的時候 將代理物件 轉換成需要的物件
三.具體程式碼
註解:
package com.example.writeaopdemo.annotion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jet
*/
@Retention(RetentionPolicy.RUNTIME) //保留時間長短
@Target(value = {ElementType.TYPE})//使用範圍、介面、類、列舉、註解
public @interface Aspect {
}
package com.example.writeaopdemo.annotion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 方法切入點
*/
@Retention(RetentionPolicy.RUNTIME)//保留時間長短
@Target(value = {ElementType.METHOD}) //使用範圍 方法
public @interface PointCut {
/**
* 全類名_方法名
* @return
*/
String value();
}
測試bean測試Aspect
package com.example.writeaopdemo.domain;
import com.example.writeaopdemo.annotion.Aspect;
import com.example.writeaopdemo.annotion.PointCut;
import com.example.writeaopdemo.proxy.AbsMethodAdvance;
@Aspect
public class TestAspect extends AbsMethodAdvance {
/**
*全類名 方法名
*/
@PointCut("com.example.writeaopdemo.domain.Test_doSomeThing")
public void testAspect(){
}
@Override
public void doBefore() {
System.out.println("do before");
}
@Override
public void doAfter() {
System.out.println("do after");
}
}
代理類(在建立代理物件的同時攔截方法的執行實現before和after邏輯)
package com.example.writeaopdemo.proxy;
import com.example.writeaopdemo.util.StringUtils;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author Jet
*/
public abstract class AbsMethodAdvance implements MethodInterceptor {
/**
* 要被代理的物件
*/
private Object targetObject;
/**
* 被代理的方法名
*/
private String proxyMethodName;
public Object createProxyObject(Object target){
this.targetObject = target;
//該類用於生成代理物件
Enhancer enhancer = new Enhancer();
//設定目標類為代理物件的父類
enhancer.setSuperclass(this.targetObject.getClass());
//設定回撥用物件為本身
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable{
Object result;
String proxyMethod = getProxyMethodName();
if(StringUtils.isNotBlank(proxyMethod) && proxyMethod.equals(method.getName())){
doBefore();
}
//執行攔截的方法
result = methodProxy.invokeSuper(proxy,args);
if(StringUtils.isNotBlank(proxyMethod) && proxyMethod.equals(method.getName())){
doAfter();
}
return result;
}
public abstract void doBefore();
public abstract void doAfter();
public String getProxyMethodName(){
return proxyMethodName;
}
public void setProxyMethodName(String proxyMethodName){
this.proxyMethodName = proxyMethodName;
}
}
載入類
package com.example.writeaopdemo.util;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* @author Jet
*/
public class ClassUtil {
/**
* 獲取類載入器
*
* @return
*/
public static ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
/**
* 載入類
* 需要提供類名是否初始化標誌
* 初始化是指知否執行靜態程式碼塊
*
* @param className
* @param isInitialized
* @return
*/
public static Class<?> loadClass(String className, boolean isInitialized) {
Class<?> cls;
try {
cls = Class.forName(className, isInitialized, getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return cls;
}
/**
* 載入類(預設將初始化類)
*
* @param className
* @return
*/
public static Class<?> loadClass(String className) {
return loadClass(className, true);
}
public static Set<Class<?>> getClassSet(String packageName) throws IOException {
Set<Class<?>> classSet = new HashSet<>();
try {
Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
//Enumeration相當於老闆迭代器
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url != null) {
String protocol = url.getProtocol();//獲得URL的協議
if (protocol.equals("file")) {
//轉碼
String packagePath = URLDecoder.decode(url.getFile(), "UTF-8");//轉碼為utf-8的格式
addClass(classSet, packagePath, packageName);
} else if (protocol.equals("jar")) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();//解析Jar檔案
if (jarURLConnection != null) {
JarFile jarFile = jarURLConnection.getJarFile();
if (jarFile != null) {
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String jarEntryName = jarEntry.getName();
if (jarEntryName.endsWith(".class")) {
String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
doAddClass(classSet, className);
}
}
}
}
}
}
}
} catch (IOException e) {
throw e;
}
return classSet;
}
private static void addClass(Set<Class<?>> classSet,String packagePath,String packageName){
// System.out.println("packageName: " + packageName);
File[] files = new File(packagePath).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(".class") || file.isDirectory());
}
});
for(File file : files){
String fileName = file.getName();
if(file.isFile()){
String className = fileName.substring(0,fileName.lastIndexOf("."));
if(StringUtils.isNotBlank(packageName)){
className = packageName + "." + className;
}
//新增
doAddClass(classSet,className);
}else{
//子目錄
String subPackagePath = fileName;
if(StringUtils.isNotBlank(packagePath)){
subPackagePath = subPackagePath + "/" + subPackagePath;
}
String subPackageName = fileName;
if(StringUtils.isNotBlank(packageName)){
subPackageName = packageName + "." + subPackageName;
}
addClass(classSet,subPackagePath,subPackageName);
}
}
}
private static void doAddClass(Set<Class<?>> classSet,String className){
Class<?> cls = loadClass(className,false);
classSet.add(cls);
}
}
通過反射機制建立例項呼叫方法和設定成員變數的值
package com.example.writeaopdemo.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionUtil {
/**
* 建立例項
*/
public static Object newInstance(Class<?> cls){
Object instance;
try{
instance = cls.newInstance();
}catch (Exception e){
throw new RuntimeException();
}
return instance;
}
/**
* 建立例項 根據類名
*/
public static Object newInstance(String className){
Class<?> cls = ClassUtil.loadClass(className);
return newInstance(cls);
}
/**
* 呼叫方法
*/
public static Object invokeMethod(Object obj, Method method,Object... args){
Object result;
try{
method.setAccessible(true);
result = method.invoke(obj,args);
}catch (Exception e){
throw new RuntimeException(e);
}
return result;
}
/**
* 設定成員變數的值
*/
public static void setField(Object obj, Field field,Object value){
try{
field.setAccessible(true);
field.set(obj,value);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
字串處理類
package com.example.writeaopdemo.util;
/**
* @author Jet
*/
public class StringUtils {
public static boolean isNotBlank(String str){
if(str != null && str.trim().length() > 0){
return true;
}else{
return false;
}
}
public static boolean isBlank(String str){
return !isNotBlank(str);
}
}
找到切點切面設定代理初始化容器類
package com.example.writeaopdemo;
import com.example.writeaopdemo.annotion.Aspect;
import com.example.writeaopdemo.annotion.PointCut;
import com.example.writeaopdemo.proxy.AbsMethodAdvance;
import com.example.writeaopdemo.util.ClassUtil;
import com.example.writeaopdemo.util.ReflectionUtil;
import org.apache.tools.ant.taskdefs.EchoXML;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Jet
*/
public class ApplicationContext {
/**
* 存放代理類的集合
*/
public static ConcurrentHashMap<String,Object> proxyBeanMap = new ConcurrentHashMap<String, Object>();
static {
initAopBeanMap("com.example.writeaopdemo.domain");
}
/**
* 初始化容器
* @param basePath
*/
public static void initAopBeanMap(String basePath){
try{
Set<Class<?>> classSet = ClassUtil.getClassSet(basePath);
for(Class clazz : classSet){
if(clazz.isAnnotationPresent(Aspect.class)){
Method[] methods = clazz.getMethods();
for(Method method : methods){
if(method.isAnnotationPresent(PointCut.class)){
//找到切點
PointCut pointCut = (PointCut)method.getAnnotations()[0];
String pointCutStr = pointCut.value();
//System.out.println("pointCutStr:" + pointCutStr);
String[] pointCutArr = pointCutStr.split("_");
//被代理的類名
String className = pointCutArr[0];
//System.out.println("className:" + className);
//被代理的方法名
String methodName = pointCutArr[1];
// System.out.println("methodName:" + methodName);
//根據切點 建立被代理物件
Object targeObj = ReflectionUtil.newInstance(className);
//根據切面類建立代理者
AbsMethodAdvance proxyer = (AbsMethodAdvance)ReflectionUtil.newInstance(clazz);
//設定代理的方法
proxyer.setProxyMethodName(methodName);
Object object = proxyer.createProxyObject(targeObj);
if(object != null){
proxyBeanMap.put(targeObj.getClass().getSimpleName().toLowerCase(),object);
}
}
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
Main類執行
import com.example.writeaopdemo.ApplicationContext;
import com.example.writeaopdemo.domain.Test;
import java.util.concurrent.ConcurrentHashMap;
public class Main {
public static void main(String[] args){
//模擬容器初始化
ApplicationContext applicationContext = new ApplicationContext();
ConcurrentHashMap<String,Object> proxyBeanMap = ApplicationContext.proxyBeanMap;
//生成代理物件,預設為該類名的小寫
Test test =(Test)proxyBeanMap.get("test");
test.doSomeThing();
System.out.println("------------");
test.doWithNotProxy();
}
}
相關推薦
手寫實現Spring AOP
Spring IOC、AOP可謂是spring中兩大王牌 上次我們手寫了IOC,那麼今天我們來手寫一下AOP 我是學習他的寫法,然後將一些我不懂的程式碼進行了註釋解釋,應該可以更加適合小白學習吧。 一.準備 首先我們都知道Spring AOP的核心技
手寫實現Spring IOC
Spring中有兩大核心內容: 一.IOC 二.AOP 今天我手寫實現了IOC,來總結一下IOC的原理和實現方式 首先IOC底層所用到的技術 1>xml配置檔案 2>dom4j解析xml 3>工廠設計模式 4>反射 5&
透徹理解Spring事務設計思想之手寫實現
數據庫操作 cal 了解 hashmap 個數 這一 use action 管道 前言 事務,是描述一組操作的抽象,比如對數據庫的一組操作,要麽全部成功,要麽全部失敗。事務具有4個特性:Atomicity(原子性),Consistency(一致性),Isolation(隔離
學習Spring原始碼:手寫實現spinrgmvc
1.關於spring ioc容器: spring ioc容器是什麼呢? 我們可以理解為將東西存放起來的東西。比如將java物件存放在ioc容器中。 簡單的說就是 ioc容器等於 ====>>> Map<Strin
【手寫系列】透徹理解Spring事務設計思想之手寫實現
事務,是描述一組操作的抽象,比如對資料庫的一組操作,要麼全部成功,要麼全部失敗。事務具有4個特性:Atomicity(原子性),Consistency(一致性),Isolation(隔離性),Durability(永續性)。在實際開發中,我們對事務應用最多就是在資料庫操作這
手寫一個Spring框架(不含AOP)
spring 手寫分三個階段: 1.配置階段: web.xml配置 servlet初始化 2.初始化階段: 載入配置檔案 ioc容器初始化 掃描相關的類 類例項化,並注入ioc容器 將url路徑和相關method進行對映關聯 3執行階段 dopost作為入
使用aspectJ實現Spring AOP的兩種方式
classpath .org 導入 ntc www. 之前 oid 方式 public 方式一:基於aspectJ的XML配置 方式二:基於aspectJ的註解方式 基於aspectJ的XML配置 1) 引入相關jar包 2) 創建Spr
理解數據庫連接池底層原理之手寫實現
ring cda color 要去 分配 .com 管理 roc tex 前言 數據庫連接池的基本思想是:為數據庫連接建立一個“緩沖池”,預先在池中放入一定數量的數據庫連接管道,需要時,從池子中取出管道進行使用,操作完畢後,在將管道放入池子中,從而避免了頻繁的向數據庫申請資
Spring AOP的實現:Spring AOP攔截器呼叫的實現
上次我說到關於ProxyFactoryBean物件的getObject方法返回了一個代理物件。剩下的AOP實現過程就和jdk的代理模式相同。通過呼叫這個代理物件的方法(這個方法和目標物件是相同的),但是實際上是呼叫了invoke方法,通過反射來實現方法的增強。 關於jdk如何實現代理模式的反
react深入 - 手寫實現react-redux api
簡介:簡單實現react-redux基礎api react-redux api回顧 <Provider store>把store放在context裡,所有子元件可以直接拿到store資料 使元件層級中的 connect() 方法都能夠獲得 Redux store 根元件應該巢狀在 &
【手寫系列】純手寫實現一個高可用的RPC
前言 在實際後臺服務開發中,比如訂單服務(開發者A負責)需要呼叫商品服務(開發者B負責),那麼開發者B會和A約定呼叫API,以介面的形式提供給A。通常都是B把API上傳到Maven私服,然後B開始寫API的實現,A只需要引入API依賴進行開發即可。 訂單
【原創】自己手寫實現Boost序列化簡易版
設計思路 在與多個系統進行網路互動時,序列化是不可缺少的技術。編寫一個C++語言的序列化實現,是練習運用模板超程式設計的絕佳案例,理解C++模板是如何"面向編譯期程式設計"的(業內好像沒有這個說法)。序列化物件處理基礎資料型別和類型別,boost的序列化功能劃分得更細緻,基本支援了C++語言
手寫實現滿足 Promise/A+ 規範的 Promise
最近看了 Promise/A+ 的規範,嘗試實現了一個滿足 promises-aplus-tests 測試的 Promise 類,在實現規範的過程中,對於 Promise 本身也加深了理解,這篇文章就將我的實現過程分享出來。 本文的程式碼倉庫在這裡,歡迎 Star~。 前置知識 Prom
理解資料庫連線池底層原理之手寫實現
前言 資料庫連線池的基本思想是:為資料庫連線建立一個“緩衝池”,預先在池中放入一定數量的資料庫連線管道,需要時,從池子中取出管道進行使用,操作完畢後,在將管道放入池子中,從而避免了頻繁的向資料庫申請資源,釋放資源帶來的效能損耗。在如今的分散式系統當中,系統的QPS瓶頸往往就
Java動態代理概述和手寫實現動態代理
一:前提 Spring中最重要的兩種思想:控制反轉IOC(Inversion of Control)和麵向切面程式設計AOP(Aspect-Oriented Programming),而AOP最重要的原理就是動態代理,今天我們談一下動態代理。動態代理顧名思義是代替別人做某些
對HashMap的思考及手寫實現
前言HashMap是Java中常用的集合,而且HashMap的一些思想,對於我們平時解決業務上的一些問題,在思路上有幫助,基於此,本篇部落格將分析HashMap底層設計思想,並手寫一個迷你版的HashMap! 對HashMap的思考 HashMap底層資料結構 第
從 0 開始手寫一個 Spring MVC 框架,向高手進階
Spring框架對於Java後端程式設計師來說再熟悉不過了,以前只知道它用的反射實現的,但瞭解之後才知道有很多巧妙的設計在裡面。如果不看Spring的原始碼,你將會失去一次和大師學習的機會:它的程式碼規範,設計思想很值得學習。 我們程式設計師大部分人都是野路子,不懂什麼
Anroid分析Andfix原理手寫實現
前言 目前市面上對於熱修復一線網際網路企業大概分為三家:1、阿里 2、騰訊 3、美團 而這三家公司提供的開源庫,給了我們android開發者一些答案,今天我們瞭解一下阿里的andfix,目前andfix已經在16底停止維護了,新推出的是sophix,兼
手寫實現RPC框架(帶註冊中心)
可能這個圖不太準確,但是大體意思就是服務端在註冊中心中註冊服務,客戶端在註冊中心獲取服務地址進行呼叫,中間可能還會有一些LB等: 定義一個註冊服務的頂層介面IRegistryCenter: package dgb.nospring.myrpc.regist
手寫實現HashMap程式碼
瞭解HashMap底層=陣列+連結串列 HashMap底層原始碼通過 連結串列法 來解決hash衝突,找到hash值對應位置不為空,維護一個連結串列 ThreadLocal底層原始碼,ThreadLocalMap中通過 線性探測 解決hash衝突,找到hash值