Spring框架是J2EE开发中一个使用广泛的框架,它使得dao和service层的维护更加便利。Spring框架有两个重要的特征,一个是IOC,另一个是AOP。我们在这里主要介绍IOC,以及IOC中涉及的主要技术。

  IOC(Inversion Of Control),控制反转是将创建对象的控制权从程序员手中转向Spring框架。Spring框架在创建对象时使用了DI技术。

  DI(Dependency Injection),依赖注入主要是通过setter注入和构造器注入,产生一个类Class的bean(实例)。DI主要是通过动态代理和反射技术来实现的。

  我们先来了解代理的概念。何为代理呢?顾名思义,代理可以帮助我们去完成某项事务,而且可以在完成事务中增强、完善相应的功能。举例来说,我国多个地区已经实施适龄公民可以直接报考驾照,但在实际中,我们在考取驾照时,往往会委托一家驾校帮助我们报考驾照,在这之中,驾校就充当了我们报考驾照的代理角色。显然,在很多时候,通过代理可以帮助我们节省时间,获得更加便捷的服务。

  其实,我们在Java开发中运用代理,与生活中的代理概念相似。

,  接下来,我们在介绍动态代理之前,先了解静态代理。我们接下来以生活中报考驾照为例写一个简单的案例,来了解静态代理。

  

package bkjz;
/**
* 考驾照的人
*/
public class Examiner {
//身份证号,姓名
private String id,name; public Examiner(String id, String name) {
this.id = id;
this.name = name;
} @Override
public String toString() {
return "考生(姓名:" +name+
",身份证号:" + id + ")";
}
} ****************** package bkjz;
/**
* 考驾照,核心业务功能接口
* 传递入一个具体报考驾照的人
*/
public interface GetDriverLicense {
public void getDriverLicense(Examiner examiner);
} ****************** package bkjz.impl;
import bkjz.Examiner;
import bkjz.GetDriverLicense;
/**
* 实现报考驾照的核心功能接口的类
*/
public class GetDriverLicenseImpl implements GetDriverLicense {
@Override
public void getDriverLicense(Examiner examiner) {
System.out.println("报考驾照核心业务:您的各项报名指标合格,可以报考驾照");
}
} ****************** package bkjz.impl;
import bkjz.Examiner;
import bkjz.GetDriverLicense;
/**
* 驾校代理,帮助报考者报考驾照
*/
public class OrgProxy implements GetDriverLicense {
//调用核心功能的接口实现类
private GetDriverLicense getDriverLicense;
//构造器传参
public OrgProxy(GetDriverLicenseImpl getDriverLicense){
this.getDriverLicense=getDriverLicense;
}
@Override
public void getDriverLicense(Examiner examiner) {
System.out.println("增强功能1:你好,"+examiner+",我来帮你收集报名需要提交哪些资料");
System.out.println("增强功能2:你好,"+examiner+",我来帮你推荐体检医院,帮你节省时间");
System.out.println("增强功能3:你好,"+examiner+",我来帮你到车管所递交报名资料,帮你节省时间");
System.out.println("===========================");
getDriverLicense.getDriverLicense(examiner);
System.out.println("===========================");
System.out.println("增强功能4:你好,"+examiner+",报名成功后,我帮你把报考所需身份资料的原件取回,帮你节省时间");
System.out.println("增强功能5:你好,"+examiner+",我来帮你安排培训学习时间,帮你通过考试");
System.out.println("增强功能6:你好,"+examiner+",你通过驾照考试后,我帮你邮寄驾照,帮你节省时间");
}
} ************************* package bkjz; import bkjz.impl.GetDriverLicenseImpl;
import bkjz.impl.OrgProxy; /**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//一个准备报考驾照的考生
Examiner examiner=new Examiner("320923198901142757","张三");
//找个驾校来报考驾照,以及安排后续的学习取照
OrgProxy orgProxy=new OrgProxy(new GetDriverLicenseImpl());
orgProxy.getDriverLicense(examiner);
}
}

  执行测试类后,运行结果如下:

增强功能1:你好,考生(姓名:张三,身份证号:320923198901142757),我来帮你收集报名需要提交哪些资料
增强功能2:你好,考生(姓名:张三,身份证号:320923198901142757),我来帮你推荐体检医院,帮你节省时间
增强功能3:你好,考生(姓名:张三,身份证号:320923198901142757),我来帮你到车管所递交报名资料,帮你节省时间
===========================
报考驾照核心业务:您的各项报名指标合格,可以报考驾照
===========================
增强功能4:你好,考生(姓名:张三,身份证号:320923198901142757),报名成功后,我帮你把报考所需身份资料的原件取回,帮你节省时间
增强功能5:你好,考生(姓名:张三,身份证号:320923198901142757),我来帮你安排培训学习时间,帮你通过考试
增强功能6:你好,考生(姓名:张三,身份证号:320923198901142757),你通过驾照考试后,我帮你邮寄驾照,帮你节省时间

  可见,通过代理的确能够为我们带来很多便利,还可以增强很多功能。实际上,我们平时遇到的过滤器、拦截器从本质上来说,也是通过代理的方式实现的,通过代理增强一些功能,从而控制对被代理的业务的访问。当然,过滤器、拦截器运用的代理技术要比静态代理更灵活。

  静态代理有个弊端,那就是如果具体业务改变时,我们需要改写其他的代理类,在需要代理的功能很多时,就会显著增加我们的工作量,不利于效率的提升。为此,我们引入动态代理的概念。

  所谓动态代理,显然,我们需要增加一些增强型的功能时,不必再多次创建代理类,而是动态地进行相应的扩展即可。我们通过案例来看基于业务接口的动态代理。

package test2_proxy.proxy;

import test2_proxy.service.OrderServiceImpl;
import test2_proxy.service.UserServiceImpl; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* 基于接口的动态代理
* 对于一些需要重复执行的验证功能或下一步操作,
* 在核心方法执行前后都会执行增强的功能
*/
public class ServiceProxy { public static Object newProxyInstance(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态增强功能1:登录权限认证");
System.out.println("动态增强功能2:黑名单验证");
System.out.println("=====================");
Object invoke = method.invoke(target, args);//执行被代理的核心业务功能
System.out.println("=====================");
System.out.println("动态增强功能3:进入订单中心");
System.out.println("此处省略一万行(●—●)...");
return invoke;
}
});
}
} *********************** package test2_proxy.service; /**
* 用户登录、注册、找回密码等一系列服务的接口
*/
public interface UserService {
public void testUserService();
} ************************ package test2_proxy.service; import org.springframework.stereotype.Service; /**
* 实现用户服务接口的类
*/
@Service
public class UserServiceImpl implements UserService {
@Override
public void testUserService() {
System.out.println("UserService核心功能");
}
} *************************** package test2_proxy.service; /**
* 订单服务接口,如下单、取消订单、放入购物车等
*/
public interface OrderService { public void testOrderService();
} *************************** package test2_proxy.service; /**
* 实现订单核心功能接口的类
*/
public class OrderServiceImpl implements OrderService {
@Override
public void testOrderService() {
System.out.println("OrderService核心功能");
}
} *************************** package test2_proxy; import test2_proxy.proxy.ServiceProxy;
import test2_proxy.service.OrderService;
import test2_proxy.service.OrderServiceImpl;
import test2_proxy.service.UserService;
import test2_proxy.service.UserServiceImpl; /**
* 测试类
*/
public class Test {
public static void main(String[] args) {
UserService userService=new UserServiceImpl();
userService = (UserService)ServiceProxy.newProxyInstance(userService);
userService.testUserService();
System.out.println("***************************");
OrderService orderService=new OrderServiceImpl();
orderService = (OrderService)ServiceProxy.newProxyInstance(orderService);
orderService.testOrderService();
}
}

  上述代码执行完后,执行结果如下:

动态增强功能1:登录权限认证
动态增强功能2:黑名单验证
=====================
UserService核心功能
=====================
动态增强功能3:进入订单中心
此处省略一万行(●—●)...
***************************
动态增强功能1:登录权限认证
动态增强功能2:黑名单验证
=====================
OrderService核心功能
=====================
动态增强功能3:进入订单中心
此处省略一万行(●—●)...

  可见,动态代理比静态代理更加灵活,它的核心是 java.lang.reflect.Proxy代理类,该类位于reflect反射包下,可知动态代理当中是用了反射的技术来实现的。

  接下来,我们再看基于类实现的动态代理。此处需要在项目中添加 cglib-2.2.2.jar包的依赖,运用cglib的方法,使代码更简洁。我们通过案例来实现。

  

package test3_proxy_cglib.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method;
import java.util.Arrays; /**
* 动态代理类
*/
public class ServiceProxy {
public static Object newProxyInstance(Object target) {
//1.工具类
Enhancer enhancer = new Enhancer();
//2.设置父类,传递类对象
enhancer.setSuperclass(target.getClass());
//3.设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib增强功能1");
System.out.println("cglib增强功能2");
System.out.println("此处省略一万行(●—●)...");
System.out.println("==================");
return method.invoke(target, objects);
}
});
//4.功能增强后,返回代理对象
return enhancer.create();
}
} **************************** package test3_proxy_cglib; /**
* 用户服务功能类
*/
public class UserService {
public void testUserService(){
System.out.println("UserService核心功能");
}
} ***************************** package test3_proxy_cglib; /**
* 订单服务功能类
*/
public class OrderService {
public void testOrderService(){
System.out.println("OrderService核心功能");
}
} ********************************** package test3_proxy_cglib; import test3_proxy_cglib.proxy.ServiceProxy; /**
* 测试类
*/
public class Test {
public static void main(String[] args) {
UserService userService=new UserService();
userService= (UserService)ServiceProxy.newProxyInstance(userService);
userService.testUserService();
System.out.println("***************************");
OrderService orderService=new OrderService();
orderService=(OrderService)ServiceProxy.newProxyInstance(orderService);
orderService.testOrderService();
}
}

  上述代码的执行结果如下:

cglib增强功能1
cglib增强功能2
此处省略一万行(●—●)...
==================
UserService核心功能
***************************
cglib增强功能1
cglib增强功能2
此处省略一万行(●—●)...
==================
OrderService核心功能

  接下来,我们看下反射。

  Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。Java关于反射的定义主要在 java.lang.reflect包中。Java反射需要用到Class对象,Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

  Class 类的实例表示正在运行的 Java 应用程序中的类和接口,也就是jvm中有N多的实例,每个类都有对应的Class对象(包括基本数据类型)。Class 没有公共构造方法,Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是说,Class 对象不需要我们自己去创建,JVM已经帮我们创建好了。

  在使用反射时,我们需要先获取Class对象。获取Class对象有三种方式:

  第一,Object对象.getClass()

  第二,任何数据类型(包括基本数据类型)都有一个静态static的class属性

  第三,通过Class类的静态方法forName(String className)来获取  Class.forName(String className)    ------>[这是我们常用的获取类对象的方法]

  需要注意的是,在程序运行期间,一个类只有一个Class对象产生,该Class对象由JVM管理。获得了类对象,我们就随之可以使用这个类对象的所有属性和方法。

  通过反射,我们可以获取一个类的任意方法(包括构造方法)和属性,从而充分利用一个类所提供的一切资源。

  另外,我们在使用各类集合(如List,Set,Map)时,为了指明集合中元素的类型,通常会用到泛型,泛型是作用于程序编译期,编译过后,泛型的约束就会失效;反射则作用于程序编译期之后的运行期,因此,我们可以通过反射来越过泛型的检查。我们通过下面的案例来看:  

package test9;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 泛型作用于编译期,会在编译之前对程序进行检查
* 编译期过后,泛型约束失效
* 反射作用于运行期
* 演示通过反射越过泛型的检查
*/
public class Test {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.先声明一个List集合,元素泛型约束为String类型
List<String> strList=new ArrayList<>();
//2.向集合中添加String型的元素
strList.add("aa");
strList.add("bb");
//注:在编译期之前,向集合中添加非泛型元素,程序会在编译期就报错
//strList.add(123);
//这时,我们通过反射来越过泛型检查
Class strListClass = strList.getClass();//先获取Class对象
//获取add方法,为了保障程序清晰,在main方法上抛出异常
Method method = strListClass.getMethod("add", Object.class);
//通过反射调用add方法,添加一个int型数据
method.invoke(strList,123);
System.out.println("strList="+strList);
}
}

  上述代码打印结果如下:

strList=[aa, bb, 123]

  可见,定义元素泛型为String的List集合成功越过了泛型检查,添加了一个int型数据。

Spring之IOC,DI,动态代理,反射的更多相关文章

  1. spring(IOC)动态代理

    姓名:黄于霞      班级:软件151 1.引入Spring IOC的核心jar包,创建IOC的配置文件beans.xml,内容如下: 1 <?xml version="1.0&qu ...

  2. 杨晓峰-Java核心技术-6 动态代理 反射 MD

    目录 第6讲 | 动态代理是基于什么原理? 典型回答 考点分析 知识扩展 反射机制及其演进 动态代理 精选留言 Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAnd ...

  3. Spring中的JDK动态代理

    Spring中的JDK动态代理 在JDK1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在动态代理是实现AOP的绝好底层 ...

  4. spring---aop(4)---Spring AOP的CGLIB动态代理

    写在前面 前面介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程. CGLib全称为Code Generation Library,是一个强 ...

  5. Spring Boot实践——Spring AOP实现之动态代理

    Spring AOP 介绍 AOP的介绍可以查看 Spring Boot实践——AOP实现 与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改 ...

  6. Spring事务Transactional和动态代理(二)-cglib动态代理

    系列文章索引: Spring事务Transactional和动态代理(一)-JDK代理实现 Spring事务Transactional和动态代理(二)-cglib动态代理 Spring事务Transa ...

  7. Spring中的cglib动态代理

    Spring中的cglib动态代理 cglib:Code Generation library, 基于ASM(java字节码操作码)的高性能代码生成包 被许多AOP框架使用 区别于JDK动态代理,cg ...

  8. Spring AOP中的动态代理

    0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  Spring AOP中的动态代理机制 2.1  ...

  9. 转:Spring AOP中的动态代理

    原文链接:Spring AOP中的动态代理 0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  S ...

  10. Spring事务Transactional和动态代理(一)-JDK代理实现

    系列文章索引: Spring事务Transactional和动态代理(一)-JDK代理实现 Spring事务Transactional和动态代理(二)-cglib动态代理 Spring事务Transa ...

随机推荐

  1. DjangoORM字段参数介绍

    参数介绍: 字段的参数:     null:             ->db是否可以为空     default:          ->默认值     primary_key:     ...

  2. 【刷题】洛谷 P4716 【模板】最小树形图

    题目背景 这是一道模板题. 题目描述 给定包含 \(n\) 个结点, \(m\) 条有向边的一个图.试求一棵以结点 \(r\) 为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 \(r\ ...

  3. 【JavaScript】函数表达式

    一.前言        接着上一篇的内容,继续学习JavaScript. 二.内容       函数的声明 function functionName(arg0,arg1,arg2){ //函数体 } ...

  4. Winform中的Treeview动态绑定数据库

    http://bbs.csdn.net/topics/370139193 SQL code   ? 1 2 3 4 5 6 CREATE TABLE [dbo].[Company] (     [Id ...

  5. jbpm3.2创建数据库

    因为jbpm3.2的sql脚本有问题,所以我们通过查询来执行是有问题的,所以这里我们通过java代码来执行,这个是没有问题的. 参考博文: http://blog.csdn.net/sz_bdqn/a ...

  6. libiop网络库数据结构和基础知识

    最近朋友推荐,学习了libiop这个网络库,作者封装的很全面,代码很简洁 适合初学者学习基于事件驱动的网络io 先看看iop_def.h, 这里面定义了常用的数据结构 tag_iop_base_t 主 ...

  7. Highcharts.js -纯javasctipt图表库初体验

    一.highcharts简介以及引入 highcharts作为免费提供给个人学习.个人网站和非商业用途使用的前端图表演示插件的确使用起来十分方便和轻便.在我最近完成一个需求的时候用到了它, 它的兼容性 ...

  8. 前端PHP入门-028-文件操作-掌握级别

    作为刚入门我们员经常会干的意见事情是ctrl+c和ctrl+v,鼠标右键删除文件.会control+c(或右键)复制.粘贴文件以及新建文件,还可以设置文件的是否为只读文件等等 可不可以写入修改配置文件 ...

  9. 在Eclipse中开发使用Spring IOC的JUnit/TestNG测试用例之详解

    转载自:http://blog.csdn.net/radic_feng/article/details/6740438 我们期望能像在产品代码中一样,在测试用例中使用的bean也由Spring Con ...

  10. Tomcat不能启动注意事项

    Server.xml中的<Context></Context>不能有相同的两个或多个,把相同的只能保留一个