Spring框架是J2EE开发中一个使用普遍的框架,它使得dao和service层的维护更加便利。Spring框架有两个重要的特征,一个是IOC,另外一个是AOP。咱们在这里主要介绍IOC,以及IOC中涉及的主要技术。java
IOC(Inversion Of Control),控制反转是将建立对象的控制权从程序员手中转向Spring框架。Spring框架在建立对象时使用了DI技术。程序员
DI(Dependency Injection),依赖注入主要是经过setter注入和构造器注入,产生一个类Class的bean(实例)。DI主要是经过动态代理和反射技术来实现的。spring
咱们先来了解代理的概念。何为代理呢?顾名思义,代理能够帮助咱们去完成某项事务,并且能够在完成事务中加强、完善相应的功能。举例来讲,我国多个地区已经实施适龄公民能够直接报考驾照,但在实际中,咱们在考取驾照时,每每会委托一家驾校帮助咱们报考驾照,在这之中,驾校就充当了咱们报考驾照的代理角色。显然,在不少时候,经过代理能够帮助咱们节省时间,得到更加便捷的服务。框架
其实,咱们在Java开发中运用代理,与生活中的代理概念类似。jvm
, 接下来,咱们在介绍动态代理以前,先了解静态代理。咱们接下来以生活中报考驾照为例写一个简单的案例,来了解静态代理。ide
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型数据。