Spring
-
简介
- spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
- Spring为简化开发而生,让程序员只需关注核心业务的实现,尽可能的不再关注非业务逻辑代码(事务控制,安全日志等)
-
IoC(Inversion of Control)
- 这是一种设计思想,可以降低代码间的耦合度,符合依赖倒置原则
- 核心思想:
- 将对象的创建权交出去
- 将对象和对象之间关系的管理权交出去
- 对象的创建和对象之间关系的维护都由第三方容器来负责
-
DI(Dependency Injection)
- 实现IOC的方式有多种,其中比较重要的是DI(依赖注入)
- 依赖注入用于实现对象之间关系的建立
- 依赖注入的两种方式:
- set注入;执行set方法给属性赋值
- 构造方法注入:执行构造方法给属性赋值
-
spring八大模块:
-
Bean:
-
spring配置文件
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userBean" class="com.powernode.spring6.bean.User" /> </beans>
-
id:代表对象的唯一标识,看作一个人的身份证
-
class:指定创建对象的类名(全限定类目)
-
-
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Object userBean = applicationContext.getBean("userBean");
-
BeanFactory是Spring容器的超级接口。ApplicationContext是BeanFactory的子接口。
-
spring的ioc容器底层实际上使用了工厂模式
-
spring是通过调用类的无参数构造方法来创建对象的,所以要想让spring给你创建对象,必须保证无参数构造方法是存在的。
-
spring创建对象的原理:
-
// dom4j解析beans.xml文件,从中获取class的全限定类名 // 通过反射机制调用无参数构造方法创建对象 Class clazz = Class.forName("com.powernode.spring6.bean.User"); Object obj = clazz.newInstance();
-
-
Spring底层的IoC实现原理:XML解析+工厂模式+反射机制
-
创建好的对象储存到HashMap中:
- Map<String,Object>
- key是id
- value是bean
-
-
Set注入
-
注入外部Bean:
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao" /> <bean id="userServiceBean" class="com.powernode.spring6.service.UserService"> <property name="userDao" ref="userDaoBean"/> </bean> </beans>
-
bean定义到外面,在property标签中使用ref属性进行注入
-
-
注入内部Bean:
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userServiceBean" class="com.powernode.spring6.service.UserService"> <property name="userDao"> <!--在property标签中使用嵌套的bean标签,这就是内部Bean--> <bean class="com.powernode.spring6.dao.UserDao"/> </property> </bean> </beans>
-
-
注入简单类型:
-
package com.powernode.spring6.bean; /** * @author shanglinsong * @version 1.0 * @className com.powernode.spring6.bean.User * @date 2022/11/9 * @since 1.0 */ public class User { private int age; @Override public String toString() { return "User{" + "age=" + age + '}'; } public void setAge(int age) { this.age = age; } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userBean" class="com.powernode.spring6.bean.User"> <!--<property name="age" value="20"/>--> <property name="age"> <!--如果像这种int类型的属性,我们称为简单类型,这种简单类型在注入的时候要使用value属性,不能使用ref--> <value>20</value> </property> </bean> </beans>
-
注意:如果给简单类型赋值,使用value属性或value标签,而不是ref。
-
通过BeanUtils类源码分析得知,简单类型包括:
- 基本数据类型及其包装类
- String或其他CharSequence子类
- Number子类
- Date子类
- Enum子类
- URL和URI
- Temporal子类
- Local
- Class
-
注入数组k,.
-
数组元素是简单类型
-
package com.powernode.spring6.bean; import java.util.Arrays; public class Person { private String[] favoriteFoods; @Override public String toString() { return "Person{" + "favoriteFoods=" + Arrays.toString(favoriteFoods) + '}'; } public void setFavoriteFoods(String[] favoriteFoods) { this.favoriteFoods = favoriteFoods; } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="personBean" class="com.powernode.spring6.bean.Person"> <property name="favoriteFoods"> <array> <value>鸡排</value> <value>汉堡</value> <value>鹅肝</value> </array> </property> </bean> </beans>
-
-
-
数组元素非简单类型
-
package com.powernode.spring6.bean; public class Goods { private String name; @Override public String toString() { return "Goods{" + "name='" + name + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Goods(String name) { this.name = name; } public Goods() { } }
-
package com.powernode.spring6.bean; import java.util.Arrays; public class Order { // 一个订单中有多个商品 private Goods[] goods; @Override public String toString() { return "Order{" + "goods=" + Arrays.toString(goods) + '}'; } public void setGoods(Goods[] goods) { this.goods = goods; } public Order() { } public Order(Goods[] goods) { this.goods = goods; } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="goodsBean1" class="com.powernode.spring6.bean.Goods"> <property name="name" value="香蕉"/> </bean> <bean id="goodsBean2" class="com.powernode.spring6.bean.Goods"> <property name="name" value="葡萄"/> </bean> <bean id="orderBean" class="com.powernode.spring6.bean.Order"> <property name="goods"> <array> <!--这里使用ref标签即可--> <ref bean="goodsBean1" /> <ref bean="goodsBean2" /> </array> </property> </bean> </beans>
-
-
要点:
- 元素是简单类型,使用value标签
- 元素非简单类型,使用ref标签
-
-
注入List集合
-
list集合:有序可重复
-
package com.powernode.spring6.bean; import java.util.List; public class People { // 一个人有多个名字 private List<String> names; @Override public String toString() { return "People{" + "names=" + names + '}'; } public void setNames(List<String> names) { this.names = names; } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="peopleBean" class="com.powernode.spring6.bean.People"> <property name="names"> <!--list集合有序可重复--> <list> <value>铁锤</value> <value>张三</value> <value>张三</value> <value>张三</value> <value>狼</value> </list> </property> </bean> </beans>
-
注意:注入List集合的时候使用list标签,如果List集合中是简单类型使用value标签,反之使用ref标签。
-
-
注入Set集合
-
Set集合:无序不可重复
-
package com.powernode.spring6.bean; import java.util.List; import java.util.Set; /** */ public class People { // 一个人有多个名字 private List<String> names; // 一个人有多个电话 private Set<String> phones; @Override public String toString() { return "People{" + "names=" + names + ", phones=" + phones + '}'; } public void setPhones(Set<String> phones) { this.phones = phones; } public void setNames(List<String> names) { this.names = names; } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="peopleBean" class="com.powernode.spring6.bean.People"> <property name="names"> <!--list集合有序可重复--> <list> <value>铁锤</value> <value>张三</value> <value>张三</value> <value>张三</value> <value>狼</value> </list> </property> <property name="phones"> <!--Set集合:无序不可重复--> <set> <!--非简单类型可以使用ref,简单类型使用value--> <value>110</value> <value>110</value> <value>120</value> <value>120</value> <value>119</value> <value>119</value> </set> </property> </bean> </beans>
-
要点:
- 使用标签
- set集合中元素是简单类型的使用value标签,反之使用ref标签。
-
-
注入Map集合
-
package com.powernode.spring6.bean; import java.util.List; import java.util.Map; import java.util.Set; public class People { // 一个人有多个名字 private List<String> names; // 一个人有多个电话 private Set<String> phones; // 一个人有多个地址 private Map<Integer, String> addrs; @Override public String toString() { return "People{" + "names=" + names + ", phones=" + phones + ", addrs=" + addrs + '}'; } public void setAddrs(Map<Integer, String> addrs) { this.addrs = addrs; } public void setPhones(Set<String> phones) { this.phones = phones; } public void setNames(List<String> names) { this.names = names; } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="peopleBean" class="com.powernode.spring6.bean.People"> <property name="names"> <!--list集合有序可重复--> <list> <value>铁锤</value> <value>张三</value> <value>张三</value> <value>张三</value> <value>狼</value> </list> </property> <property name="phones"> <!--Set集合:无序不可重复--> <set> <!--非简单类型可以使用ref,简单类型使用value--> <value>110</value> <value>110</value> <value>120</value> <value>120</value> <value>119</value> <value>119</value> </set> </property> <property name="addrs"> <map> <!--如果key不是简单类型,使用 key-ref 属性--> <!--如果value不是简单类型,使用 value-ref 属性--> <entry key="1" value="北京大兴区"/> <entry key="2" value="上海浦东区"/> <entry key="3" value="深圳宝安区"/> </map> </property> </bean> </beans>
-
要点:
- 使用
- 如果key是简单类型,使用 key 属性,反之使用 key-ref 属性。
- 如果value是简单类型,使用value属性,反之使用value-ref属性
-
-
注入Properties
-
java.util.Properties继承java.util.Hashtable,所以Propertise也是个Map集合
-
package com.powernode.spring6.beans; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class People { private Properties properties; public void setProperties(Properties properties) { this.properties = properties; } @Override public String toString() { return "People{" + "properties=" + properties + ", addrs=" + addrs + ", phones=" + phones + ", names=" + names + '}'; } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="peopleBean" class="com.powernode.spring6.beans.People"> <property name="properties"> <props> <prop key="driver">com.mysql.cj.jdbc.Driver</prop> <prop key="url">jdbc:mysql://localhost:3306/spring</prop> <prop key="username">root</prop> <prop key="password">123456</prop> </props> </property> </bean> </beans>
-
要点:使用标签嵌套标签完成
-
-
注入的值中含有特殊符号
-
XML中有五个特殊字符,分别是:<、>、‘、”、&
-
以上五个字符在XML中会被当作语法的一部分进行解析,如果这些特殊符号出现在注入的字符串中会报错
-
解决方案:
- 第一种:转义字符
-
-
- ```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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mathBean" class="com.powernode.spring6.beans.Math">
<property name="result" value="2 < 3"/>
</bean>
</beans>
```
- 第二种:将含有特殊字符的字符串放到:<![CDATA[]]>
- ```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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mathBean" class="com.powernode.spring6.beans.Math">
<property name="result">
<!--只能使用value标签-->
<value><![CDATA[2 < 3]]></value>
</property>
</bean>
</beans>
```
- 注意:使用CDATA,不能使用value属性,只能使用value标签
-
注入null和空字符串
-
注入空字符串
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="vipBean" class="com.powernode.spring6.beans.Vip"> <!--空串的第一种方式--> <!--<property name="email" value=""/>--> <!--空串的第二种方式--> <property name="email"> <value/> </property> </bean> </beans>
-
注入null
-
第一种:不赋值
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="vipBean" class="com.powernode.spring6.beans.Vip" /> </beans>
-
第二种:使用
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="vipBean" class="com.powernode.spring6.beans.Vip"> <property name="email"> <null/> </property> </bean> </beans>
-
-
基于xml的自动装配:Autowired = "byType"根据类型自动装配
-
package com.powernode.spring6.service; import com.powernode.spring6.dao.AccountDao; public class AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void save(){ accountDao.insert(); } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--byType表示根据类型自动装配--> <bean id="accountService" class="com.powernode.spring6.service.AccountService" autowire="byType"/> <bean class="com.powernode.spring6.dao.AccountDao"/> </beans>
-
-
基于xml的自动装配:Autowired = "byName"根据名称自动装配
-
package com.powernode.spring6.service; import com.powernode.spring6.dao.UserDao; public class UserService { private UserDao aaa; // 这个set方法非常关键 public void setAaa(UserDao aaa) { this.aaa = aaa; } public void save(){ aaa.insert(); } }
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/> <bean id="aaa" clss="com.powernode.spring6.dao.UserDao"/> </beans>
-
-
无论是byName还是byType都是基于set方法
-
Spring引入外部属性配置文件
-
package com.powernode.spring6.beans; import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; public class MyDataSource implements DataSource { @Override public String toString() { return "MyDataSource{" + "driver='" + driver + '\'' + ", url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } private String driver; private String url; private String username; private String password; public void setDriver(String driver) { this.driver = driver; } public void setUrl(String url) { this.url = url; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } }
-
引入jdbc.propertise文件
-
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/spring username=root password=root123
-
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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
-
spring配置文件使用jdbc.propertise文件
-
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="jdbc.properties"/> <bean id="dataSource" class="com.powernode.spring6.beans.MyDataSource"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> </beans>
-
-
Spring中Bean的作用域
-
singleton(默认情况下):单例
- 默认情况下,Bean对象的创建是在初始化Spring上下文的时候就完成的。
-
prototype:原型(多例)
- 这样Spring会在每一次执行getBean()方法的时候创建Bean对象,调用几次则创建几次。
-
其他scope还有6种
-
request:一个请求对应一个Bean。仅限于在WEB应用中使用
-
session:一个会话对应一个Bean。仅限于在WEB应用中使用
-
global session:portlet应用中专用的。如果在Servlet的WEB应用中使用global session的话,和session一个效果。
-
application:一个应用对应一个Bean。仅限于在WEB应用中使用
-
websocket:一个websocket生命周期对应一个Bean。仅限于在WEB应用中使用
-
自定义的scope(了解)
-
第一步:自定义Scope。(实现Scope接口)
- spring内置了线程范围的类:org.springframework.context.support.SimpleThreadScope,可以直接用。
-
第二步:将自定义的Scope注册到Spring容器中
-
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="myThread"> <bean class="org.springframework.context.support.SimpleThreadScope"/> </entry> </map> </property> </bean>
-
使用Scope。
-
<bean id="sb" class="com.powernode.spring6.beans.SpringBean" scope="myThread" />
-
-
-
-
-
GoF 23种设计模式
-
设计模式:一种可以被重复利用的解决方案
-
23种设计模式分为三种
-
工厂模式通常有三种形态:
- 第一种:简单工厂模式(Simple Factory):不属于23种设计模式之一。简单工厂模式又叫做:静态 工厂方法模式。简单工厂模式是工厂方法模式的一种特殊实现。
- 第二种:工厂方法模式(Factory Method):是23种设计模式之一。
- 第三种:抽象工厂模式(Abstract Factory):是23种设计模式之一。
-
简单工厂模式:
-
角色包括三个:
- 抽象产品
- 具体产品
- 工厂
-
package com.powernode.factory; /** * 武器(抽象产品角色) **/ public abstract class Weapon { /** * 所有的武器都有攻击行为 */ public abstract void attack(); }
-
package com.powernode.factory; /** * 坦克(具体产品角色) * **/ public class Tank extends Weapon{ @Override public void attack() { System.out.println("坦克开炮!"); } }
-
package com.powernode.factory; /** * 战斗机(具体产品角色) **/ public class Fighter extends Weapon{ @Override public void attack() { System.out.println("战斗机投下原子弹!"); } }
-
package com.powernode.factory; /** * 匕首(具体产品角色) **/ public class Dagger extends Weapon{ @Override public void attack() { System.out.println("砍他丫的!"); } }
-
package com.powernode.factory; /** * 工厂类角色 **/ public class WeaponFactory { /** * 根据不同的武器类型生产武器 * @param weaponType 武器类型 * @return 武器对象 */ public static Weapon get(String weaponType){ if (weaponType == null || weaponType.trim().length() == 0) { return null; } Weapon weapon = null; if ("TANK".equals(weaponType)) { weapon = new Tank(); } else if ("FIGHTER".equals(weaponType)) { weapon = new Fighter(); } else if ("DAGGER".equals(weaponType)) { weapon = new Dagger(); } else { throw new RuntimeException("不支持该武器!"); } return weapon; } }
-
package com.powernode.factory; public class Client { public static void main(String[] args) { Weapon weapon1 = WeaponFactory.get("TANK"); weapon1.attack(); Weapon weapon2 = WeaponFactory.get("FIGHTER"); weapon2.attack(); Weapon weapon3 = WeaponFactory.get("DAGGER"); weapon3.attack(); } }
-
优点:
- 客户端程序不需要关心对象的创建细节,需要哪个对象时,只需要向工厂索要即可,初步实现了责任的分离。客户端只负责“消费”,工厂负责“生产”。生产和消费分离。
-
缺点:
- 工厂类集中了所有产品的创造逻辑,形成一个无所不知的全能类,有人把它叫做上帝类。显然工厂类非常关键,不能出问题,一旦出问题,整个系统瘫痪。
- 不符合OCP开闭原则,在进行系统扩展时,需要修改工厂类。
-
Spring中的BeanFactory就使用了简单工厂模式。
-
-
工厂方法模式:
-
工厂方法模式既保留了简单工厂模式的优点,同时又解决了简单工厂模式的缺点。
-
角色包括四个:
- 抽象工厂
- 具体工厂
- 抽象产品
- 具体产品
-
package com.powernode.factory; /** * 武器类(抽象产品角色) **/ public abstract class Weapon { /** * 所有武器都有攻击行为 */ public abstract void attack(); }
-
package com.powernode.factory; /** * 具体产品角色 **/ public class Gun extends Weapon{ @Override public void attack() { System.out.println("开枪射击!"); } }
-
package com.powernode.factory; /** * 具体产品角色 **/ public class Fighter extends Weapon{ @Override public void attack() { System.out.println("战斗机发射核弹!"); } }
-
package com.powernode.factory; /** * 武器工厂接口(抽象工厂角色) **/ public interface WeaponFactory { Weapon get(); }
-
package com.powernode.factory; /** * 具体工厂角色 **/ public class GunFactory implements WeaponFactory{ @Override public Weapon get() { return new Gun(); } }
-
package com.powernode.factory; /** * 具体工厂角色 **/ public class FighterFactory implements WeaponFactory{ @Override public Weapon get() { return new Fighter(); } }
-
package com.powernode.factory; public class Client { public static void main(String[] args) { WeaponFactory factory = new GunFactory(); Weapon weapon = factory.get(); weapon.attack(); WeaponFactory factory1 = new FighterFactory(); Weapon weapon1 = factory1.get(); weapon1.attack(); } }
-
优点
- 在进行功能扩展的时候,不需要修改之前的源代码,显然工厂方法模式符合OCP原则。
- 一个调用者想创建一个对象,只要知道其名称就可以了。
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
-
缺点:
- 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
-
-
抽象工厂模式(了解)
-
Bean的实例化方式
-
Spring为Bean的实例化提供了4种方式
- 第一种:通过构造方法实例化
- 第二种:通过简单工厂模式实例化
- 第三种:通过factory-bean实例化
- 第四种:通过FactoryBean接口实例化
-
通过构造方法实例化
-
我们之前一直使用的就是这种方式。默认情况下,会调用Bean的无参数构造方法。
package com.powernode.spring6.bean; public class User { public User() { System.out.println("User类的无参数构造方法执行。"); } }
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userBean" class="com.powernode.spring6.bean.User"/> </beans>
package com.powernode.spring6.test; import com.powernode.spring6.bean.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringInstantiationTest { @Test public void testConstructor(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); User user = applicationContext.getBean("userBean", User.class); System.out.println(user); } }
-
-
通过简单工厂模式实例化
-
public class Vip { }
package com.powernode.spring6.bean; public class VipFactory { public static Vip get(){ return new Vip(); } }
<bean id="vip" class="com.powernode.spring6.bean.VipFactory" factory-method="get"/>
@Test public void testSimpleFactory(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); Vip vip = applicationContext.getBean("vip", Vip.class); System.out.println(vip); }
-
-
通过factory-bean实例化
-
本质上是工厂方法模式实例化
-
package com.powernode.spring6.bean; public class Order { }
package com.powernode.spring6.bean; public class OrderFactory { public Order get(){ return new Order(); } }
<bean id="orderFactory" class="com.powernode.spring6.bean.OrderFactory"/> <bean id="order" factory-bean="orderFactory" factory-method="get"/>
@Test public void testSelfFactoryBean(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); Order order = applicationContext.getBean("order", Order.class); System.out.println(order); }
第二个和第三个的差异就在于工厂中的方法是静态还是实例
-
-
通过FactoryBean接口实例化
-
在Spring中,当你编写的类直接实现FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了
-
factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法。
-
package com.powernode.spring6.bean; public class Person { }
-
package com.powernode.spring6.bean; import org.springframework.beans.factory.FactoryBean; public class PersonFactoryBean implements FactoryBean<Person> { @Override public Person getObject() throws Exception { return new Person(); } @Override public Class<?> getObjectType() { return null; } @Override public boolean isSingleton() { return FactoryBean.super.isSingleton(); } }
-
<bean id="person" class="com.powernode.spring6.bean.PersonFactoryBean"/>
-
@Test public void testFactoryBean(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); Person person = applicationContext.getBean("person", Person.class); System.out.println(person); Person person1 = applicationContext.getBean("person", Person.class); System.out.println(person1); }
-
FactoryBean在Spring中是一个接口。被称为“工厂Bean”。“工厂Bean”是一种特殊的Bean。所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的
-
-
BeanFactory和FactoryBean的区别
-
-
Bean的生命周期
- - 第一个黑点指的是实例化Bean - 第二个黑点指的是销毁Bean - 注意: - 第一:只有正常关闭spring容器,bean的销毁方法才会被调用。 - 第二:ClassPathXmlApplicationContext类才有close()方法。 - 第三:配置文件中的init-method指定初始化方法,destory-method指定销毁方法 - 第四:配置文件中配置Bean后处理器 ```xml <!--配置Bean后处理器,这个后处理器作用于当前配置文件中所有的bean--> <bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/> 这个LogBeanPostProcessor是自己编写的实现了BeanPostProcessor类 ``` - 第五:Aware相关的接口包括:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware - 当Bean实现了BeanNameAware,Spring会将Bean的名字传递给Bean。 - 当Bean实现了BeanClassLoaderAware,Spring会将加载该Bean的类加载器传递给Bean。 - 当Bean实现了BeanFactoryAware,Spring会将Bean工厂对象传递给Bean。 - ```java package com.powernode.spring6.bean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.*; /** * @author shanglinsong * @version 1.0 * @className com.powernode.spring6.bean.User * @date 2022/11/19 * @since 1.0 */ public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean { private String name; public User() { System.out.println("1. 实例化Bean"); } public void setName(String name) { this.name = name; System.out.println("2. Bean属性赋值"); } public void initBean(){ System.out.println("6. 初始化Bean"); } public void destroyBean(){ System.out.println("10. 销毁Bean"); } @Override public void setBeanClassLoader(ClassLoader classLoader) { System.out.println("3. 类加载器" + classLoader); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("3. Bean工厂" + beanFactory); } @Override public void setBeanName(String name) { System.out.println("3. Bean名字" + name); } @Override public void destroy() throws Exception { System.out.println("9. DisposableBean destroy执行"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("5. afterPropertiesSet执行"); } } ``` - ```java package com.powernode.spring6.bean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class LogBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("4. Bean后处理器的before方法执行,即将开始初始化"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("7. Bean后处理器的after方法执行,已完成初始化"); return bean; } } ``` - ```java @Test public void testFiveLifeCycle(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); User user = applicationContext.getBean("user", User.class); System.out.println("8. 使用Bean"); // 只有正常关闭spring容器才会执行销毁方法 ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext; context.close(); } ```
-
Bean的作用域不同,管理方式不同
- 对于singleton作用域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;
- 而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。
-
自己new的对象如何让Spring管理
-
@Test public void testBeanRegister(){ // 自己new对象 Customer customer = new Customer(); // 创建 默认可列表的BeanFactory 对象 DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // 注册Bean factory.registerSingleton("customer", customer); // 从spring容器中获取Bean Customer customerBean = factory.getBean("customer", Customer.class); System.out.println(customerBean); }
-
-
Bean的循环依赖问题
-
A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。
-
package com.powernode.spring6.bean; public class Husband { private String name; private Wife wife; @Override public String toString() { return "Husband{" + "name='" + name + '\'' + ", wife=" + wife.getName() + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setWife(Wife wife) { this.wife = wife; } }
package com.powernode.spring6.bean; public class Wife { private String name; private Husband husband; @Override public String toString() { return "Wife{" + "name='" + name + '\'' + ", husband=" + husband.getName() + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setHusband(Husband husband) { this.husband = husband; } }
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="wife" class="com.powernode.spring6.bean.Wife"> <property name="name" value="小娴"/> <property name="husband" ref="husband"/> </bean> <bean id="husband" class="com.powernode.spring6.bean.Husband"> <property name="name" value="小松"/> <property name="wife" ref="wife"/> </bean> </beans>
-
通过测试得知:在singleton + set注入的情况下,循环依赖是没有问题的。Spring可以解决这个问题。
-
prototype下的set注入产生的循环依赖
- 通过测试得知,当循环依赖的所有Bean的scope="prototype"的时候,产生的循环依赖,Spring是无法解决的,会出现BeanCurrentlyInCreationException异常。
- ,两个Bean,如果其中一个是singleton,另一个是prototype,是没有问题的。
- 主要原因是因为通过构造方法注入导致的:因为构造方法注入会导致实例化对象的过程和对象属性赋值的过程没有分离开,必须在一起完成导致的。
- Spring为什么可以解决set + singleton模式下循环依赖?
- 根本的原因在于:这种方式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作分开去完成。
- 实例化Bean的时候:调用无参数构造方法来完成。此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。
- 给Bean属性赋值的时候:调用setter方法来完成。
- 两个步骤是完全可以分离开去完成的,并且这两步不要求在同一个时间点上完成。
- 也就是说,Bean都是单例的,我们可以先把所有的单例Bean实例化出来,放到一个集合当中(我们可以称之为缓存),所有的单例Bean全部实例化完成之后,以后我们再慢慢的调用setter方法给属性赋值。这样就解决了循环依赖的问题。
- Spring只能解决setter方法注入的单例bean之间的循环依赖。ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在创建ClassA对象后,不需要等给属性赋值,直接将其曝光到bean缓存当中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题
-
-
注解式开发
-
声明Bean的注解
- @Component
- @Controller
- @Service
- @Repository(@Mapper)
- 通过源码可以看到,@Controller、@Service、@Repository这三个注解都是@Component注解的别名。
-
负责注入的注解
- @Value:注入的属性为简单类型
- @Value注解可以出现在属性上、setter方法上、以及构造方法的形参上
- @Autowired:注入的属性为非简单类型,先根据类型装配
- @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。
- @Qualifier
- @Autowired注解和@Qualifier注解联合起来才可以根据名称进行装配,在@Qualifier注解中指定Bean名称。
- @Resoure:注入的属性为非简单类型,先根据名称装配
- @Value:注入的属性为简单类型
-
@Resource和@Autowired注解有什么区别?
- @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
- @Autowired注解是Spring框架自己的。
- @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
- @Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
- @Resource注解用在属性上、setter方法上。
- @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。
-
全注解式开发
-
所谓的全注解开发就是不再使用spring配置文件了。写一个配置类来代替配置文件。
-
@Configuration @ComponentScan({"com.powernode.spring6.dao", "com.powernode.spring6.service"}) public class Spring6Configuration { }
-
@Test public void testNoXml(){ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class); UserService userService = applicationContext.getBean("userService", UserService.class); userService.save(); }
-
-
-
GoF之代理模式
-
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。
-
代理模式中的角色:
- 代理类(代理主题)
- 目标类(真实主题)
- 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,代理类和目标类要有共同的行为,也就是实现共同的接口
-
-
代理模式在代码实现上,包括两种形式:
- 静态代理
- 动态代理
-
静态代理
-
package com.powernode.mall.service; public interface OrderService { /** * 生成订单 */ void generate(); /** * 查看订单详情 */ void detail(); /** * 修改订单 */ void modify(); }
-
package com.powernode.mall.service.impl; import com.powernode.mall.service.OrderService; public class OrderServiceImpl implements OrderService { @Override public void generate() { try { Thread.sleep(1234); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("订单已生成"); } @Override public void detail() { try { Thread.sleep(2413); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("订单信息如下:******"); } @Override public void modify() { try { Thread.sleep(1010); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("订单已修改"); } }
-
package com.powernode.mall.service.impl;
-
/
public class OrderServiceImplSub extends OrderServiceImpl{
@Override
public void generate() {
long begin = System.currentTimeMillis();
super.generate();
long end = System.currentTimeMillis();
System.out.println(“耗费时长为” + (end - begin) + “ms”);
}@Override public void detail() { long begin = System.currentTimeMillis(); super.detail(); long end = System.currentTimeMillis(); System.out.println("耗费时长为" + (end - begin) + "ms"); } @Override public void modify() { long begin = System.currentTimeMillis(); super.modify(); long end = System.currentTimeMillis(); System.out.println("耗费时长为" + (end - begin) + "ms"); } } ```
-
@Test public void testImplSub(){ OrderService target = new OrderServiceImplSub(); target.generate(); target.detail(); target.modify(); }
-
这种方式可以解决,但是存在两个问题:
- 第一个问题:假设系统中有100个这样的业务类,需要提供100个子类,并且之前写好的创建Service对象的代码,都要修改为创建子类对象。
- 第二个问题:由于采用了继承的方式,导致代码之间的耦合度较高。
-
第二种方案:使用静态代理
-
package com.powernode.mall.service; public class OrderServiceProxy implements OrderService{ // 目标对象 private OrderService orderService; // 通过构造方法将目标对象传递给代理对象 public OrderServiceProxy(OrderService orderService) { this.orderService = orderService; } @Override public void generate() { long begin = System.currentTimeMillis(); orderService.generate(); long end = System.currentTimeMillis(); System.out.println("耗费时长为" + (end - begin) + "ms"); } @Override public void detail() { long begin = System.currentTimeMillis(); orderService.detail(); long end = System.currentTimeMillis(); System.out.println("耗费时长为" + (end - begin) + "ms"); } @Override public void modify() { long begin = System.currentTimeMillis(); orderService.modify(); long end = System.currentTimeMillis(); System.out.println("耗费时长为" + (end - begin) + "ms"); } }
-
这种方式的优点:符合OCP开闭原则,同时采用的是关联关系,所以程序的耦合度较低。所以这种方案是被推荐的。
-
@Test public void testProxy(){ // 创建目标对象 OrderService target = new OrderServiceImpl(); // 创建代理对象 OrderService proxy = new OrderServiceProxy(target); // 调用代理对象的代理方法 proxy.generate(); proxy.detail(); proxy.modify(); }
-
以上就是代理模式中的静态代理,其中OrderService接口是代理类和目标类的共同接口。OrderServiceImpl是目标类。OrderServiceProxy是代理类。
-
如果系统中业务接口很多,一个接口对应一个代理类,显然也是不合理的,会导致类爆炸。怎么解决这个问题?动态代理可以解决。因为在动态代理中可以在内存中动态的为我们生成代理类的字节码。代理类不需要我们写了。类爆炸解决了,而且代码只需要写一次,代码也会得到复用。
-
-
动态代理
-
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
-
在内存当中动态生成类的技术常见的包括:
- JDK动态代理技术:只能代理接口。
- CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
- Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
-
JDK 动态代理
-
package com.powernode.mall.service; /** * 订单接口 * @author shanglinsong * @version 1.0 * @className com.powernode.mall.service.OrderService * @date 2022/11/22 * @since 1.0 */ public interface OrderService { /** * 生成订单 */ void generate(); /** * 查看订单详情 */ void detail(); /** * 修改订单 */ void modify(); }
-
package com.powernode.mall.service.impl; import com.powernode.mall.service.OrderService; public class OrderServiceImpl implements OrderService { @Override public void generate() { try { Thread.sleep(1234); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("订单已生成"); } @Override public void detail() { try { Thread.sleep(2413); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("订单信息如下:******"); } @Override public void modify() { try { Thread.sleep(1010); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("订单已修改"); } }
-
package com.powernode.mall; import com.powernode.mall.service.OrderService; import com.powernode.mall.service.impl.OrderServiceImpl; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { // 第一步:创建目标对象 OrderService target = new OrderServiceImpl(); // 第二步:创建代理对象 OrderService orderServiceProxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 调用处理器对象); // 第三步:调用代理对象的代理方法 orderServiceProxy.detail(); orderServiceProxy.modify(); orderServiceProxy.generate(); } }
-
以上第二步创建代理对象是需要大家理解的:
OrderService orderServiceProxy = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), 调用处理器对象);
-
这行代码做了两件事:
- 第一件事:在内存中生成了代理类的字节码
- 第二件事:创建代理对象
-
Proxy类全名:java.lang.reflect.Proxy。这是JDK提供的一个类(所以称为JDK动态代理)。主要是通过这个类在内存中生成代理类的字节码。
-
其中newProxyInstance()方法有三个参数:
-
第一个参数:类加载器。在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。
-
第二个参数:接口类型。代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。
-
第三个参数:调用处理器。这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。显然这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。
-
InvocationHandler接口的实现类
-
package com.powernode.mall.service; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TimerInvocationHandler implements InvocationHandler { // 目标对象 private Object target; // 通过构造方法来传目标对象 public TimerInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 目标执行之前增强 long begin = System.currentTimeMillis(); // 调用目标对象的目标方法 Object reValue = method.invoke(target, args); // 目标执行之后增强 long end = System.currentTimeMillis(); System.out.println("耗时" + (end - begin) + "ms"); // 一定要记得返回! return reValue; } }
-
InvocationHandler接口中有一个方法invoke,这个invoke方法上有三个参数:
- 第一个参数:Object proxy。代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
- 第二个参数:Method method。目标方法。
- 第三个参数:Object[] args。目标方法调用时要传的参数。
-
-
@Test public void testDynamicProxy(){ // 第一步:创建目标对象 OrderService target = new OrderServiceImpl(); // 第二步:创建代理对象 OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TimerInvocationHandler(target)); // 第三步:调用代理对象的代理方法 orderServiceProxy.generate(); orderServiceProxy.detail(); orderServiceProxy.modify(); }
-
注意:当你调用代理对象的代理方法的时候,注册在InvocationHandler接口中的invoke()方法会被调用。也就是上面代码第10 11 12行,这三行代码中任意一行代码执行,注册在InvocationHandler接口中的invoke()方法都会被调用。
-
还可以改进:
-
package com.powernode.mall.util; import com.powernode.mall.service.TimerInvocationHandler; import java.lang.reflect.Proxy; public class ProxyUtil { /** * 返回方法执行计时器代理对象 * @param target * @return */ public static Object newProxyInstance(Object target){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TimerInvocationHandler(target)); } }
-
@Test public void testDynamicProxy(){ // 第一步:创建目标对象 OrderService target = new OrderServiceImpl(); // 第二步:创建代理对象 OrderService orderServiceProxy = (OrderService) ProxyUtil.newProxyInstance(target); // 第三步:调用代理对象的代理方法 orderServiceProxy.generate(); orderServiceProxy.detail(); orderServiceProxy.modify(); }
-
-
CGLIB动态代理:
-
CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。
-
使用CGLIB,需要引入它的依赖
-
package com.powernode.mall.service; /** * @author shanglinsong * @version 1.0 * @className com.powernode.mall.service.UserService * @date 2022/11/22 * @since 1.0 */ public class UserService { public void login(){ System.out.println("用户正在登录系统......"); } public void logout(){ System.out.println("用户正在退出系统......"); } }
-
@Test public void testCGLIBDynamicProxy(){ // 创建字节码增强器 Enhancer enhancer = new Enhancer(); // 告诉cglib要继承哪个类 enhancer.setSuperclass(UserService.class); // 设置回调接口 enhancer.setCallback(方法拦截器对象); // 生成源码,编译class,加载到JVM,并创建代理对象 UserService userServiceProxy = (UserService) enhancer.create(); userServiceProxy.login(); userServiceProxy.logout(); }
-
package com.powernode.mall.service; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class TimerMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // 前增强 long begin = System.currentTimeMillis(); // 调用目标 Object reValue = methodProxy.invokeSuper(target, objects); // 后增强 long end = System.currentTimeMillis(); System.out.println("耗时" + (end - begin) + "ms"); // 一定要返回! return reValue; } }
-
MethodInterceptor接口中有一个方法intercept(),该方法有4个参数:
- 第一个参数:目标对象
- 第二个参数:目标方法
- 第三个参数:目标方法调用时的实参
- 第四个参数:代理方法
-
-
@Test public void testCGLIBDynamicProxy(){ // 创建字节码增强器 Enhancer enhancer = new Enhancer(); // 告诉cglib要继承哪个类 enhancer.setSuperclass(UserService.class); // 设置回调接口 enhancer.setCallback(new TimerMethodInterceptor()); // 生成源码,编译class,加载到JVM,并创建代理对象 UserService userServiceProxy = (UserService) enhancer.create(); userServiceProxy.login(); userServiceProxy.logout(); }
-
面向切面编程AOP
-
Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换:
-
Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换:
- 如果是代理接口,会默认使用JDK动态代理,
- 如果要代理某个类,这个类没有实现接口,就会切换使用CGLIB。
- 当然,你也可以强制通过一些配置让Spring只使用CGLIB。
-
一般一个系统当中都会有一些系统服务,例如:日志、事务管理、安全等。这些系统服务被称为:交叉业务
-
-
如果在每一个业务处理过程当中,都掺杂这些交叉业务代码进去的话,存在两方面问题:
- 第一:交叉业务代码在多个业务流程中反复出现,显然这个交叉业务代码没有得到复用。并且修改这些交叉业务代码的话,需要修改多处。
- 第二:程序员无法专注核心业务代码的编写,在编写核心业务代码的同时还需要处理这些交叉业务。
-
用一句话总结AOP:将与核心业务无关的代码独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。
-
AOP的优点:
- 代码复用性强
- 代码易维护
- 使开发者更关注业务逻辑
-
AOP的七大术语:
-
-
-
-
-
-