您的位置:首页 > 运维架构

IOC和AOP的简单实现

2015-01-28 12:33 204 查看
一直使用spring,说起来就是IOC和AOP,看过不少原理的书,但是spring的代码太多,梳理起来很困难,于是想自己实现一下,昨天下午写出代码来,分享一下。

目标:

1、使用annotation编程进行分层,有service层和dao层(mapper层)。

2、设置容器,将所有的实例注入到容器里,类似spring的applicationContext。

3、dao层使用接口,没有实现类,类似于ibitas的使用方式。

4、读取本地文件内容。

根据目标大概思考并实践了以下几点:

1、动态代理选用cglib实现,首先这是spring的实现方式,其次亲测使用jdk的反射包无法实现对接口的动态代理,无法满足上述目标3。

2、容器使用单例模式创建,保证所有的bean只有一个。

代码如下:

首先是自定义的Annotation,为了和spring的加以区别,我都加上了Fx前缀。

第一个是FxMapper,类似spring的@Repository,也就是Dao层应该加的Annotation

package com.smikevon.proxy.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by fengxiao on 15-1-27.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FxMapper {
String value();
}


第二个是FxService对应于spring的service层,类似spring的@Service。

package com.smikevon.proxy.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by fengxiao on 15-1-27.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FxService {
String value();
}


第三个FxResource对应spring里的@Resource,也就是类内饮用的注释

package com.smikevon.proxy.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by fengxiao on 15-1-27.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FxResource {
String value();
}


Annotation类全部贴完,下面贴出各层的一个示例类。

按上面顺序,第一个mapper类

package com.smikevon.proxy.dynamic;

import com.smikevon.proxy.annotations.FxMapper;
/**
* Created by fengxiao on 15-1-27.
*/
@FxMapper("fileMapper")
public interface FileMapper {
public String read(String fileDir);
public void append(String fileDir , String message);
}


第二个service类

package com.smikevon.proxy.dynamic;

import com.smikevon.proxy.annotations.FxResource;
import com.smikevon.proxy.annotations.FxService;

/**
* Created by fengxiao on 15-1-27.
*/
@FxService("fileService")
public class FileService {
String file = "rFile.txt";

@FxResource(value="fileMapper")
public FileMapper fileMapper;

public void read(){
System.out.println("hello world");
String txt = fileMapper.read(file);
System.out.println("message:"+txt);
}
}


示例类已经贴完,下面贴出容器类。容器类通过使用一个hashmap来实现容器,这里没有考虑线程安全的问题,只是为了示例。初始化使用静态内部类来实现单例模式。

package com.smikevon.proxy.dynamic;

import com.smikevon.proxy.Processors.FileMapperProcessor;
import com.smikevon.proxy.Processors.FileServiceProcessor;
import com.smikevon.proxy.annotations.FxMapper;
import com.smikevon.proxy.annotations.FxService;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
* Created by fengxiao on 15-1-27.
*/
public class FxApplicationContext {

private static final String JAVA_SOURCE_DIR = "src/main/java";
private static final String JAVA_PACKAGE_DIR = "com/smikevon/proxy/dynamic";
private static final String JAVA_PACKAGE_PATH = "com.smikevon.proxy.dynamic";

private Map<String,Object> context = new HashMap<String,Object>();

private FxApplicationContext(){}

/**
* 容器初始化
*/
public FxApplicationContext init(){
try {
File file1 = new File(JAVA_SOURCE_DIR+File.separator+JAVA_PACKAGE_DIR);
File[] files = file1.listFiles();
for(File file : files){
if(file.getName().endsWith("java")){
String name = file.getName().substring(0,file.getName().indexOf("."));
Class<?> clazz = Class.forName(JAVA_PACKAGE_PATH+"."+name);
if(clazz.getAnnotation(FxMapper.class)!=null){
String key = clazz.getAnnotation(FxMapper.class).value();
//实例化mapper的处理类
FileMapperProcessor processor = new FileMapperProcessor();
if(context.get(key) == null){
context.put(key,processor.bind(clazz));
}
}
if(clazz.getAnnotation(FxService.class)!=null){
String key = clazz.getAnnotation(FxService.class).value();
//实例化service的处理类
FileServiceProcessor processor = new FileServiceProcessor();
if(context.get(key) == null){
context.put(key,processor.bind(clazz));
}
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return this;

}

public Object getBean(String beanId){
Iterator<Map.Entry<String, Object>> iterator = context.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<String, Object> entry = iterator.next();
String key = entry.getKey();
if(key.equals(beanId)){
return entry.getValue();
}
}
return null;
}

/**
* 静态内部类方式实现单例
*/
private static class FxApplicationContextHolder{
public static FxApplicationContext instance = new FxApplicationContext();
}

public static FxApplicationContext getInstance(){
return FxApplicationContextHolder.instance;
}

}


动态代理引用了cglib包,在项目里要加上如下引用:

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>


代理类如下:

先是对有@FxMapper标记的类的动态代理方法

package com.smikevon.proxy.Processors;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Method;

/**
* Created by fengxiao on 15-1-27.
*/
public class FileMapperProcessor implements MethodInterceptor{

public Object bind(Class clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setCallback(this);
enhancer.setSuperclass(clazz);
Object obj = enhancer.create();
return obj;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
StringBuilder sb = new StringBuilder();
if(method.getName().toLowerCase().startsWith("read")){
if (objects.length > 0) {
String fileDir = (String) objects[0];
File file = new File(fileDir);
BufferedReader br = new BufferedReader(new FileReader(file));

String line = null;
while((line = br.readLine())!=null){
sb.append(line);
}
}
}
return sb.toString();
}
}


其次是对有@FxService标记的类的动态代理方法

package com.smikevon.proxy.Processors;

import com.smikevon.proxy.annotations.FxResource;
import com.smikevon.proxy.dynamic.FxApplicationContext;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
* Created by fengxiao on 15-1-27.
*/
public class FileServiceProcessor implements MethodInterceptor{

private Object target;

public Object bind(Class clazz) {
try {
target = clazz.newInstance();

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
Object obj = enhancer.create();

//将mapper的实例bean赋值给对应的引用
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
if (field.getAnnotation(FxResource.class)!=null){
String value = field.getAnnotation(FxResource.class).value();
Object object = FxApplicationContext.getInstance().getBean(value);
//看到没下面这行代码和注释掉的代码效果是一样的,反射的强大之处
field.set(target,object);
//((FileService)target).fileMapper = (FileMapper)object;
}
}
return obj;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
method.invoke(target,objects);
return null;
}
}


其实,@FxService和@FxMapper可以合并成一个Annotation,对应的动态代理类也可以合并为一个(类似Spring的@Component ),但是那样的话,每个类的处理逻辑就会比较复杂,不易理解。

下面是测试方法类:

package com.smikevon.proxy.dynamic;

import java.lang.reflect.InvocationTargetException;

/**
* Created by fengxiao on 15-1-27.
*/
public class FileDemo {

public static void main(String[] args) throws InvocationTargetException {
FxApplicationContext fxApplicationContext = FxApplicationContext.getInstance().init();
FileService fileService = (FileService)fxApplicationContext.getBean("fileService");
fileService.read();
}

}


我的文件内容(文件名fFile.txt,位置就在项目根目录):

hello , my name is Mr Read!

I love reading books!


输出结果:

hello world
message:hello , my name is Mr Read!I love reading books!


看下有没有很熟悉,和spring使用bean的方式是不是很一致? 原理原来没有什么神秘的,就是这么简单。

这里那里用到了IOC?

容器方式初始化bean,将bean的生命周期交付给容器(hashmap),而不是调用者,示例里容器的生命周期比较简单,即调用init()方法后开始,程序执行完成结束。

这里那里用到了AOP?

通过动态代理读取接口参数,获取相应文件内容,这就是aop。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: