Spring简介
sping 是一个轻量级的控制反转(IOC)和面向切面编程的(AOP)的框架
IOC
-
new对象实现操作
-
UserServiceImpl
public class UserServiceImpl implements UserService{ private UserDao userDao = new UserDaoImpl(); @Override public void execute() { userDao.execute(); } }
在通过new对象调用Dao层的方法时,处理逻辑在业务层,如果之后需要修改实现的dao层方法时必须在业务层进行修改,违背了开闭原则。所以引入了下面的用set接口来得到dao层的对象。
-
-
通过set接口实现
-
UserDaoImpl
public class UserDaoImpl implements UserDao{ @Override public void execute() { System.out.println("MySQL 执行"); } }
-
UserDao
public interface UserDao { void execute(); }
-
UserServiceImpl
public class UserServiceImpl implements UserService{ private UserDao userDao; public void setUserDao(UserDao userDao){ this.userDao = userDao; } @Override public void execute() { userDao.execute(); } }
-
UserService
public interface UserService { void execute(); }
-
Test
@Test public void test(){ UserService userService = new UserServiceImpl(); UserDao userDao = new UserDaoImpl(); ((UserServiceImpl)userService).setUserDao(userDao); userService.execute(); }
使用set注入之后,业务层不再担任创建对象的角色,程序不再有主动性,而是将该功能抛给了上一层,将控制权交给了用户。用户想怎么调用,调用哪类的方法,仅需在上一层完成即可。
-
之前程序是主动创建对象,控制权在程序员手上。使用set注入之后,程序不再有主动性,而是变成了被动接受的对象。
-
IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
Hello实例
-
实体类
public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } }
-
ApplicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 用spring来创建对象,在spring这些都成为bean 类型 变量名 = new 类型(); Hello hello = new Hello(); id = 变量名 class = new 的对象 property 相当于给对象中的属性设置一个值 value 设置值 ref 设置嵌套的bean --> <bean id="hello" class="org.example.pojo.Hello"> <property name="str" value="spring"/> </bean> </beans>
-
测试
@org.junit.Test public void test(){ // 获取spring的的上下文对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); // 我们的对象都在spring中管理,从中取出即可 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello); }
-
思考:
-
Hello对象是谁创建的?
Hello对象是由Spring创建的。
-
Hello对象的属性是怎么设置的?
Hello对象的属性是由Spring容器设置的。
-
这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:就是利用set方法来进行注入的。
IOC是一种编程思想,由主动的编程变成被动的接收。
可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。
到了现在,不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!
IOC创建对象的方式
-
默认情况下bean通过无参构造方法进行创建
public User() { System.out.println("无参构造方法执行"); }
-
在写了有参构造方法之后,多个参数需要使用下列方式进行创建bean
-
通过下标创建
<bean id="user" class="org.example.pojo.User"> <constructor-arg index="0" value="xxxyyy"/> </bean>
-
通过类型创建
<!--通过类型--> <bean id="user" class="org.example.pojo.User"> <constructor-arg type="java.lang.String" value="xxxyy"/> </bean>
-
通过参数名创建
<!--参数名--> <bean id="user" class="org.example.pojo.User"> <constructor-arg name="name" value="xxxyy"/> </bean>
-
Spring配置
-
别名
<bean id="user" class="org.example.pojo.User" name = "user2,u2"> <constructor-arg name="name" value="xxxyy"/> </bean> <alias name="user" alias="user2"/>
name也可以设置别名,还可以同时设置多个。
-
import
用于团队开发,它可以将多个配置文件,导入合并为一个。
<import resource="beans1.xml"/> <import resource="beans2.xml"/> <import resource="beans3.xml"/>
依赖注入
-
构造器注入
-
Set方式注入重点
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中所以属性由容器注入
-
Address实体类
public class Address { private String address;//省略get set }
-
Student实体类
package org.example.pojo; import java.util.*; public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; //省略get set }
-
beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 用spring来创建对象,在spring这些都成为bean 类型 变量名 = new 类型(); Hello hello = new Hello(); id = 变量名 class = new 的对象 property 相当于给对象中的属性设置一个值 value 设置值 ref 设置嵌套的bean --> <bean id="student" class="org.example.pojo.Student"> <!-- 普通注入value --> <property name="name" value="xxxxyyy"/> <!-- bean注入,ref--> <property name="address" ref="address"/> <!-- 数组 --> <property name="books"> <array> <value>春秋</value> <value>战国</value> <value>论语</value> <value>孟子</value> </array> </property> <!-- list --> <property name="hobbies"> <list> <value>篮球</value> <value>跑步</value> <value>听歌</value> </list> </property> <!-- map --> <property name="card"> <map> <entry key="身份证" value="14321321"/> <entry key="电话" value="132545461"/> </map> </property> <!-- set --> <property name="games"> <set> <value>cf</value> <value>lol</value> <value>csgo</value> </set> </property> <!-- 空值 --> <property name="wife"> <null/> </property> <!-- properties --> <property name="info"> <props> <prop key="学号">201855555</prop> <prop key="性别">男</prop> </props> </property> </bean> <bean id="address" class="org.example.pojo.Address"> <property name="address" value="西安"/> </bean> </beans>
P标签和C标签
p命名空间本质还是setter注入,c命名空间本质还是构造器注入
p
命名空间:用于简化XML配置文件中的property标签,即setter注入。对于每一个需要注入的属性,我们通常需要创建一个<property>
标签来配置它。当一个bean有大量的属性需要注入时,XML文件可能会变得臃肿和难以管理。这时,我们可以使用p
命名空间,它允许我们在<bean>
标签中直接配置property注入。c
命名空间:用于简化XML配置文件中的constructor-arg标签,即构造器注入。传统的构造器注入需要为每一个需要注入的构造器参数创建一个<constructor-arg>
标签。当一个bean有大量的构造器参数时,配置也可能会变得很大。c
命名空间解决了这个问题,它允许我们在<bean>
标签中直接配置构造器注入。
举个例子,传统的setter注入和使用p
命名空间的setter注入:
<!-- 传统的setter注入 -->
<bean id="exampleBean" class="examples.ExampleBean">
<property name="email" value="example@example.com" />
</bean>
<!-- 使用 p 命名空间的 setter 注入 -->
<bean id="exampleBean" class="examples.ExampleBean"
p:email="example@example.com" />
同样地,传统的构造器注入和使用c
命名空间的构造器注入:
<!-- 传统的构造器注入 -->
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="10" />
<constructor-arg type="int" value="20" />
</bean>
<!-- 使用 c 命名空间的构造器注入 -->
<bean id="exampleBean" class="examples.ExampleBean"
c:arg1="10" c:arg2="20"/>
最后需要注意的是,使用p
或c
命名空间需要在XML文件的顶部声明对应的XML架构:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
作用域
Scope | Description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
-
单例模式(spring默认机制)
<bean id="accountService" class="com.something.DefaultAccountService"/> <!-- the following is equivalent, though redundant (singleton scope is the default) --> <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
-
原型模式(每次从容器中get都是一个新对象)
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
-
其余的只能在web开发中使用。
Bean的自动装配
自动装配是Spring 满足bean依赖的一种方式!
Spring 会在上下文自动寻找,并自动给bean 装配属性!
在Spring 中有三种装配的方式
- 在xml 中显示的配置
- 在java中显示配置
- 隐式 的自动装配bean【重要】
<!-- 显示配置 -->
<bean id="cat" class="org.example.pojo.Cat"/>
<bean id="dog" class="org.example.pojo.Dog"/>
<bean id="people" class="org.example.pojo.People">
<property name="name" value="xxxyyy"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
ByName方法自动装配
- autowire=“byName”
- 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id!
- 弊端:set 方法后面的值和 id 相同
<bean id="cat" class="cn.bloghut.domin.Cat"/>
<bean id="dog" class="cn.bloghut.domin.Dog"/>
<!--
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id!
-->
<bean id="people" class="org.example.pojo.People" autowire="byName">
<property name="name" value="xxxyy"/>
</bean>
-
autowire=“byType”
-
会自动在容器上下文中查找,和自己对象属性类型相同的bean
-
弊端:它必须保证类型全局唯一(在IOC容器中只能由一个)。
<bean id="cat" class="cn.bloghut.domin.Cat"/> <bean id="dog11" class="cn.bloghut.domin.Dog"/> <!-- byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean --> <bean id="people" class="rg.example.pojo.People" autowire="byType"> <property name="name" value="xxxyy"/> </bean>
总结:
byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
- xml文件的配置
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
-
@Autowired
-
在属性上使用
-
在set方式上使用
-
使用Autowired 可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在(需要通过其他方式注入进容器),且符合名字byType。
-
默认情况下是 byType,如果byType无法装配,那么可以使用 @Qualifier 进行 byName 装配。
-
public class People {
@Autowired
private Dog dog;
@Autowired
private Cat cat;
private String name;
public People() {
}
public People(Dog dog, Cat cat, String name) {
this.dog = dog;
this.cat = cat;
this.name = name;
}
}
-
@Nullable
- 字段标记了这个注解,表示这个字段可以为null
-
@Qualifier
- 当我们的容器存在多个相同类型,不同名称的bean。使用@Autowired 无法完成自动装配了
- 这个时候需要使用@Qualifier 和@Autowired 注解一起使用。
- 使用@Qualifier 指定一个唯一的bean对象注入!
例如
-
@Resource
@Resource注解的装配顺序稍有不同。它首先会按照byName方式进行查找装配,如果没有找到匹配的bean,再按照byType方式进行装配。如果还是没有找到,就会抛出异常。
这里是一个简化的装配规则总结:
- @Autowired: 默认按照byType方式进行查找装配。
- @Resource: 默认按照byName方式进行查找装配,如果找不到,则按照byType方式进行装配。如果都找不到,就会抛出异常。
Spring注解开发
-
使用注解需要导入context约束
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.example"/> </beans>
bean注入使用**@Componet注解等价**于
<bean id="user" class="org.example.pojo.User">
-
注入属性用@Value
// xml文件写法: <property name="name" value="xxxyy"> @Component public class User { private String name; @Value("xxxyy") public void setName(String name){ this.name = name; } }
衍生注解
- @Componet有几个衍生注解,在web开发中,会按照mvc三层架构分层!
- @Service--------业务层注解
- @Repository—持久层注解
- @Controller-----控制层注解
作用域
@Scope(“singleton”)单例
-
总结
XML 与 注解
- xml更加万能,适用于任何场合!维护简单方便
- 注解不是自己类使用不了, 维护相对复杂
XML 与 注解最佳实践
- xml用来管理bean
- 注解只负责完成属性的注入
- 我们在使用过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
<!--开启注解扫描--> <context:component-scan base-package="org.example"/> <!--开启注解支持--> <context:annotation-config/>
Java的方式配置及Spring
JavaConfig 是Spring的 一个子项目,在Spring 4之后,它成为了新功能
-
首先定义一个类,在类上添加@Component注解,让它加载到Spring IOC容器(让Spring 管理)
- AppConfig类
/* * @Configuration 这个也是Spring容器托管,注册到容器中,因为他本来就是一个Component * @Configuration 代表这是一个配置类,就和我们之前的beans.xml一样 * */ @Configuration @ComponentScan("org.example") //@Import(AppConfig.class) 可以引入其他的配置类 public class AppConfig { /* * @Bean 注册一个bean,相当于之前写的bean标签 * 方法名相当于标签中的id属性 * 方法的返回值相当于bean标签中的class属性 * */ @Bean public User getUser(){ return new User(); //返回要注入到bean中的对象 } }
-
实体类
@Component public class User { @Value("xxxyy") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
-
测试方法
public static void main(String[] args) { // 如果完全使用了配置类的方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); User getUser = context.getBean("getUser", User.class); System.out.println(getUser); }
-
定义Java 配置类,在其类上添加@Configuration 注解,说明该类是一个配置类,这个类也会被Spring 托管,因为它本身是一个@Component
-
在AppConfig 类中 添加getUser 方法,返回一个user对象
getUser 方法上的@Bean 注解 则是注册一个bean功能,相当于
<bean id="getUser" class="cn.bloghut.domain.User">
代理模式
静态代理
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
优势
- 可以使真实角色的操作更加存粹!不用去关注一些公共的业务
- 公共交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点
- 一个真实角色就会产生一个代理角色,代码量会翻倍 开发效率变低
租房例子(静态代理)
public interface Rent {
void rent();
}
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东出租房子!!");
}
}
public class Proxy implements Rent{
private Host host;
public Proxy(){
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
host.rent();
fare();
heTong();
}
public void seeHouse(){
System.out.println("中介带你看房子");
}
public void fare(){
System.out.println("中介收取费用!");
}
public void heTong(){
System.out.println("中介签署合同");
}
}
public class Client {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
新增日志输出(静态代理)
- 有一天,公司领导要求我为 某个类的所有方法新增日志输出功能。
- 怎么做呢?
- 在原来的类上的每一个方法添加日志输出?
- 这就是改变原来人家的代码了。
- 改动原有的业务代码,在公司中是大忌!
- 有可能人家你修改了人家的代码,可能给改蹦了。
- 新增一个类,制作成本小,职责单一。
public interface UserService {
void add();
void delete();
void update();
void query();
}
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("新增用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
@Override
public void query() {
System.out.println("查询用户");
}
}
public class UserProxy implements UserService{
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
public void log(String message){
System.out.println("[debug]使用了:"+message+"方法");
}
}
public static void main(String[] args) {
UserProxy userProxy = new UserProxy();
userProxy.setUserService(new UserServiceImpl());
userProxy.add();
}
动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是我们直接写好的
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
基于接口:JDK动态代理
基于类:
cglib
java字节码实现: javasist
需要了解两个类:Proxy ; 代理InvocationHandler:调用处理程序
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。(大白话:这是一个静态类,类里边有方法得到代理类)
动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它实现了接口
通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。
代理类具有以下属性:
- 代理类是公共的,最终的,而不是抽象的,如果所有代理接口都是公共的。
- 如果任何代理接口是非公开的,代理类是非公开的,最终的,而不是抽象的 。
- 代理类的不合格名称未指定。 然而,以字符串"$Proxy"开头的类名空间应该保留给代理类。
- 一个代理类扩展了java.lang.reflect.Proxy 。
- 代理类完全按照相同的顺序实现其创建时指定的接口。
- 如果一个代理类实现一个非公共接口,那么它将被定义在与该接口相同的包中。 否则,代理类的包也是未指定的。 请注意,程序包密封不会阻止在运行时在特定程序包中成功定义代理类,并且类也不会由同一类加载器定义,并且与特定签名者具有相同的包。
- 由于代理类实现了在其创建时指定的所有接口, getInterfaces在其类对象上调用getInterfaces将返回一个包含相同列表接口的数组(按其创建时指定的顺序),在其类对象上调用getMethods将返回一个数组的方法对象,其中包括这些接口中的所有方法,并调用getMethod将在代理接口中找到可以预期的方法。
- Proxy.isProxyClass方法将返回true,如果它通过代理类 - 由Proxy.getProxyClass返回的类或由Proxy.newProxyInstance返回的对象的类 - 否则为false。
- 所述java.security.ProtectionDomain代理类的是相同由引导类装载程序装载系统类,如java.lang.Object ,因为是由受信任的系统代码生成代理类的代码。 此保护域通常将被授予java.security.AllPermission 。
- 每个代理类有一个公共构造一个参数,该接口的实现InvocationHandler ,设置调用处理程序的代理实例。 而不必使用反射API来访问公共构造函数,也可以通过调用Proxy.newProxyInstance方法来创建代理实例,该方法将调用Proxy.getProxyClass的操作与调用处理程序一起调用构造函数。
实例
public interface Rent {
void rent();
}
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东出租房子!!");
}
}
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
// 生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);
}
/*
* 处理代理实例返回结果
* @param proxy
* @param method
* @param args
* @return
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 动态代理的本质就是使用反射机制
// 在方法调用前调用
seeHouse();
Object result = method.invoke(rent, args);
heTong();
return result;
}
public void seeHouse(){
System.out.println("看房子");
}
public void heTong(){
System.out.println("签合同");
}
}
public static void main(String[] args) {
//创建真实角色
Host host = new Host();
//创建代理角色不存在
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
//设置要代理的对象
proxyInvocationHandler.setRent(host);
//获取代理对象,并强制转换
Rent proxy = (Rent)proxyInvocationHandler.getProxy();
//调用
proxy.rent();
}
通用执行代码
public class ProxyInvocationHandler implements InvocationHandler {
//1.被代理的接口
public Object target;
public void setTarget(Object target){
this.target = target;
}
//2.生成得到代理类(代理谁)
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
//3.处理代理实例,并返回结果(代用代理程序)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态获取方法名称
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
/**
* 打印日志方法
* @param msg
*/
public void log(String msg){
System.out.println("[debug]===> 执行可"+msg+"方法");
}
}
小结
静态代理
- 由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了
- 静态代理通常只代理一个类
- 静态代理事先知道要代理的是什么
动态代理
- 在程序运行时,运用反射机制动态创建而成
- 动态代理是代理一个接口下的多个实现类
- 动态代理不知道要代理什么东西,只有在运行时才知道
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。