总览
- Spring 中心学习内容 IOC、AOP, jdbcTemplate, 声明式事务
- IOC: 操控回转 , 能够办理 java 方针
- AOP : 切面编程
- JDBCTemplate : 是 spring 供给一套拜访数据库的技能, 应用性强,相对好了解
- 声明式事务: 依据 ioc/aop 完结事务办理, 了解有需求小伙伴花时刻
- IOC, AOP 是重点一同难点
Spring几个重要的概念
- Spring 能够整合其他的结构(老韩解读: Spring 是办理结构的结构)
- Spring 有两个中心的概念: IOC 和 AOP
- IOC [Inversion Of Control 回转操控]
● 传统的开发模式[JdbcUtils / 反射]
程序——>环境 程序读取环境装备,然后自己创立方针
● IOC 的开发模式 [EmpAction EmpService EmpDao Emp]
程序<—–容器 容器创立好方针,程序直接运用
- DI—Dependency Injection 依靠注入,能够了解成是 IOC 的另外叫法.
- Spring 最大的价值,经过装备,给程序供给需求运用的 web 层[Servlet(Action/Controller)]/Service/Dao/[JavaBean/entity]方针, 这个是中心价值所在,也是 ioc 的具体表现, 完结解耦
创立代码完结
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
这儿读取的是out目录下的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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
1. 装备monster方针 / JavaBean2. 在beans中能够装备多个bean
3. bean 表明地便是一个Java方针
4. class特色用于指定类的全途径 -> spring底层反射创立
5. id 特色表明Java方针在spring容器中的id, 经过id能够获取 【唯一的】
6. <property name="monsterId" value="100" /> 用于给方针赋值
-->
<bean class="com.hspedu.Spring.bean.Monster" id="monster01">
<!--
这儿底层也是经过对应特色的setter办法完结的
-->
<property name="monsterId" value="100" />
<property name="name" value="牛魔王" />
<property name="skill" value="芭蕉扇"/>
</bean>
<bean class="com.hspedu.Spring.bean.Monster" id="monster02">
<property name="monsterId" value="200" />
<property name="name" value="铁扇公主" />
<property name="skill" value="哈哈哈"/>
</bean>
</beans>
@Test
public void testSpringBean() throws Exception {
// 创立容器 ApplicationContext// 这个容器和一个装备文件关联
// ioc是重量级的方针, 耗费的资源非常的多
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
// 经过getBean() 获取对应的方针
// 默许回来的是Object, 可是运转类型是Monster
Object monster01 = ioc.getBean("monster01");
Monster monster02 = (Monster) ioc.getBean("monster01");
System.out.println("monster01 = " + monster01);
System.out.println("monster02 = " + monster02 + ", monster02的name=" + monster02.getName());
// 能够不必强转
// 运用getBean()的其他重载办法
Monster monster03 = ioc.getBean("monster01", Monster.class);
System.out.println("monster03 = " + monster03);
// 查看容器注入了那些bean方针, 会输出bean的id
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
手动完结底层
细心看看, 有助于了解
/**
* ClassName: HspApplicationContext
* Package: com.hspedu.Spring.hspapplicationcontext
*
* @Author: leikooo
* @Creat: 2023/5/17 - 15:50
* @Description: 1. 这个程序用于完结Spring的一个简略容器机制
* 2. 后边还会具体的完结
* 3. 这儿咱们经过beans.xml文件记忆解析, 并生成方针, 放入放到容器之中
* 4. 供给一个办法 getBean(id) 回来对用的方针
* 5. 只是一个开胃小点心
*/
public class HspApplicationContext {
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();
// 结构器
// 接纳容器的装备文件
public HspApplicationContext(String iocBeanXmlFile) {
SAXReader saxReader = new SAXReader();
String path = HspApplicationContext.class.getResource("/").getPath();
System.out.println(path);
try {
Document document = saxReader.read(new File(path + iocBeanXmlFile));
// 得到根元素
Element rootElement = document.getRootElement();
// 得到第一个monster01方针
Element bean = rootElement.elements("bean").get(0);
// 获取第一个monster01的相关特色
String id = bean.attributeValue("id");
String classFullPatch = bean.attributeValue("class");
// System.out.println("id = " + id);
List<Element> property = bean.elements("property");
int monsterId = Integer.parseInt(property.get(0).attributeValue("value"));
String name = property.get(1).attributeValue("value");
String skill = property.get(2).attributeValue("value");
// System.out.println("monsterId = " + monsterId);
// System.out.println("name = " + name);
// System.out.println("skill = " + skill);
// 运用反射创立方针
Class<?> aClass = Class.forName(classFullPatch);
Monster monster = (Monster) aClass.newInstance();
// 运用反射赋值
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
if ("setName".equals(method.getName())) {
method.invoke(monster, name);
} else if ("setMonsterId".equals(method.getName())) {
method.invoke(monster, monsterId);
} else if ("setSkill".equals(method.getName())) {
method.invoke(monster, skill);
}
}
// System.out.println("monster = " + monster);
// 最后把创立好的方针放入到 singletonObjectssingletonObjects.put(id, monster);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Object getBean(String id) {
return singletonObjects.get(id);
}
}
Spring 办理 Bean-IOC
Spring 装备/办理 bean 介绍
Bean包办理分为两个方面
- 创立bean方针
- 给bean注入特色
Bean的装备办法
- 依据xml文件装备办法
- 依据注解办法
依据xml文件装备办法
经过类型来获取 bean
xml装备文件
<bean class="com.hspedu.Spring.bean.Monster">
<property name="monsterId" value="1010"/>
<property name="name" value="牛魔王~"/>
<property name="skill" value="芭蕉扇~"/>
</bean>
测验文件
public void test1() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
// 必须是单个方针
Monster bean = ioc.getBean(Monster.class);
System.out.println("bean = " + bean);
}
细节
- 按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个, 否则会抛出反常NoUniqueBeanDefinitionException
- 这种办法的
应用场景
:比方 XxxAction/Servlet/Controller, 或XxxService 在一个线程中只需求一个方针实例(单例)的状况 - 教师这儿在阐明一下: 在容器装备文件(比方 beans.xml)中给特色赋值, 底层是经过setter 办法完结的, 这也是为什么咱们需求供给 setter 办法的原因
经过结构器装备 bean
<!-- 运用结构器装备monster方针
1. 这儿运用 constructor-arg 能够指定结构器
2. index 表明结构器第几个元素 索引从0开端
3. 除了index还有其他的办法
-->
--- 经过index特色指定结构器
<bean class="com.hspedu.Spring.bean.Monster" id="monster03">
<constructor-arg value="200" index="0"/>
<constructor-arg value="白骨精" index="1"/>
<constructor-arg value="吸血" index="2"/>
</bean>
--- 运用name特色来指定结构器
<bean class="com.hspedu.Spring.bean.Monster" id="monster04">
<constructor-arg value="200" name="monsterId"/>
<constructor-arg value="白骨精" name="name"/>
<constructor-arg value="吸血" name="skill"/>
</bean>
---- 运用type特色来指定结构器
<bean class="com.hspedu.Spring.bean.Monster" id="monster05">
<constructor-arg value="200" type="java.lang.Integer"/>
<constructor-arg value="白骨精" type="java.lang.String"/>
<constructor-arg value="吸血" type="java.lang.String"/>
</bean>
这儿会调用Monster的对应的全参结构器 和 无参结构器
经过 p 称号空间装备 bean
需求在xml中引进 xmlns:p=”www.springframework.org/schema/p”
<bean class="com.hspedu.Spring.bean.Monster" id="monster"
p:monsterId="100"
p:name="红孩儿"
p:skill="吐火"
/>
引证/注入其它 bean 方针
在 spring 的 ioc 容器, 能够经过 ref 来完结 bean 方针的 彼此引证
- 这儿的ref便是依靠注入, spring底层帮你完结
- 留意在spring容器中, 他作为一个全体来履行, 即使假如引证到一个bean方针, 对你的装备次序没有要求
<!-- 依靠注入
1. 这儿的ref便是依靠注入, spring底层帮你完结
2. 留意在spring容器中, 他作为一个全体来履行, 即使假如引证到一个bean方针, 对你的装备次序没有要求
3. 主张仍是按照次序写, 便于阅览
-->
<bean class="com.hspedu.Spring.DAO.MemberDAOImpl" id="memberDAO"/>
<bean class="com.hspedu.Spring.Service.MemberServiceImpl" id="memberService">
<property name="memberDAO" ref="memberDAO"/>
</bean>
look一下底层没毛病
引证/注入内部 bean 方针
运用内部类注入
<bean class="com.hspedu.Spring.Service.MemberServiceImpl" id="memberService02">
<property name="memberDAO">
<bean class="com.hspedu.Spring.DAO.MemberDAOImpl"/>
</property>
</bean>
引证/注入调集/数组类型
在Spring中,咱们能够运用XML装备文件将调集类型的特色注入到Bean中。具体完结办法如下:
- 数组类型的特色注入:
<bean id="myBean" class="com.example.MyBean">
<property name="myArray">
<array>
<value>value1</value>
<value>value2</value>
<value>value3</value>
</array>
</property>
</bean>
在上述示例中,咱们运用<array>
标签注入一个名为myArray
的字符串数组。
- List类型的特色注入:
<bean id="myBean" class="com.example.MyBean">
<property name="myList">
<list>
<value>value1</value>
<value>value2</value>
<value>value3</value>
</list>
</property>
</bean>
在上述示例中,咱们运用<list>
标签注入一个名为myList
的字符串列表。
- Set类型的特色注入:
<bean id="myBean" class="com.example.MyBean">
<property name="mySet">
<set>
<value>value1</value>
<value>value2</value>
<value>value3</value>
</set>
</property>
</bean>
在上述示例中,咱们运用<set>
标签注入一个名为mySet
的字符串调集。
- Map类型的特色注入:
<bean id="myBean" class="com.example.MyBean">
<property name="myMap">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
<entry key="key3" value="value3"/>
</map>
</property>
</bean>
在上述示例中,咱们运用<map>
标签注入一个名为myMap
的字符串键值对调集。
- Properties 类型的注入
Properties这个是Map接口下的一个具体完结类 key 是 String Vlue 也是 String 类型的
<bean id="myBean" class="com.example.MyBean">
<property name="myProperties">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
上述实例代码中, 咱们运用 <property>
标签注入给一个名为 myProperties
中
具体事例完结代码
public class Master {
private String name;
private List<Monster> monsterList;
private Map<String, Monster> monsterMap;
private Set<Monster> monsterSet;
private String[] monsterName;
// 这个 Properties 是 Hashtable 的子类 , 是 key-value 的办法
// 这儿 Properties key 和 value 都是 Stringprivate Properties pros;
private Properties pros;
// 省咯 无参结构器, 全参结构器, getter和setter办法
}
运用xml装备 Master特色
<bean class="com.hspedu.Spring.bean.Master" id="master">
<property name="name" value="台上老君"/>
<!-- 给list特色赋值-->
<property name="monsterList">
<list>
<ref bean="monster01"/>
<ref bean="monster02"/>
<ref bean="monster03"/>
<!-- 内部bean 一般不必分配id, 外部没法运用-->
<bean class="com.hspedu.Spring.bean.Monster">
<property name="name" value="老鼠精"/>
<property name="monsterId" value="404"/>
<property name="skill" value="活得长"/>
</bean>
</list>
</property>
<property name="monsterMap">
<!-- 传入map特色-->
<map>
<entry>
<key>
<value>monster03</value>
</key>
<ref bean="monster03"/>
</entry>
<!-- 能够简化-->
<entry value-ref="monster04" key="monster04"/>
</map>
</property>
<!-- 传入set -->
<property name="monsterSet">
<set>
<!-- 引证外部bean-->
<ref bean="monster01" />
<!-- 界说的内部bean-->
<bean class="com.hspedu.Spring.bean.Monster">
<property name="monsterId" value="500"/>
<property name="name" value="金角大王" />
<property name="skill" value="吐火" />
</bean>
</set>
</property>
<!-- 设置数组的值 -->
<property name="monsterName">
<!-- 这个array标签value是什么需求依据事务除理-->
<array>
<value>小妖怪</value>
<value>大妖怪</value>
<value>老妖怪</value>
</array>
</property>
<property name="pros">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
经过 util 称号空间创立 list
运用util
称号空间创立调集类型的Bean一般是在创立一些简略的数据结构时运用,例如装备文件中的一些固定数据,或许是一些开发和测验时需求运用的数据。可是,假如需求创立复杂的数据结构,或许需求在运转时动态生成数据,或许需求进行复杂的数据处理操作,主张运用Java代码来完结,这样能够更灵敏和高效地操作数据。
<!-- 界说一个util:list-->
<util:list id="myBook">
<value>三国演义</value>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
</util:list>
<bean class="com.hspedu.Spring.bean.BookStore" id="bookStore">
<!-- 能够直接在ref 标签引进 -->
<property name="bookList" ref="myBook"/>
</bean>
级联特色赋值
spring 的 ioc 容器, 能够直接给方针特色的特色赋值
设置级联特色
<bean class="com.hspedu.Spring.bean.Dept" id="dept" />
<bean class="com.hspedu.Spring.bean.Emo" id="emo">
<property name="name" value="Jack" />
<property name="dept" ref="dept" />
<property name="dept.name" value="Java开发部分" />
</bean>
称号点特色就ok
经过静态工厂获取方针
工厂类
public class MyStaticFactory {
private static Map<String, Monster> monsterMap;
// 运用静态代码块, 只会履行一次
static {
monsterMap = new HashMap<String, Monster>();
monsterMap.put("1", new Monster(1, "牛魔王", "芭蕉扇"));
monsterMap.put("2", new Monster(2, "狐狸精", "美人计"));
}
/**
* 回来对应的 monster
*/
public static Monster getMonster(String key) {
return monsterMap.get(key);
}
}
- 经过静态工厂获取
- class 是工厂的途径
- factory-method 表明的是指定工厂类是由哪一个方针回来
- constructor—-arg 的value指的是要指定回来哪一个工厂方针
<bean id="my_monster01"
class="com.hspedu.Spring.Factory.MyStaticFactory"
factory-method="getMonster">
<constructor-arg value="1"/>
</bean>
经过实例工厂获取方针
- 由于是非静态的所以需求造方针
- factory-bean 指定运用拿一个实例工厂
- factory-method 指定运用实例工厂的哪一个办法
- constructor-arg 传入的参数
<bean id="myInstanceFactory" class="com.hspedu.Spring.Factory.MyInstanceFactory" />
<bean id="my_monster02" factory-bean="myInstanceFactory" factory-method="getMonster">
<!-- 由于这个方针一开端就会创立, 所以不论获取几回都是同一个方针-->
<constructor-arg value="monster_02" />
</bean>
工厂类
public class MyInstanceFactory {
private Map<String, Monster> monster_map;
// 非静态代码块
{
monster_map = new HashMap<String, Monster>();
monster_map.put("monster_01", new Monster(100, "山公精", "吃人"));
monster_map.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));
}
public Monster getMonster(String key) {
return monster_map.get(key);
}
}
FactoryBean 装备方针 【重要】
FactoryBean
是 Spring 供给的另一种创立 Bean 的办法,与一般的 Bean 不同的是,FactoryBean
产生的 Bean 并不是经过结构函数或工厂办法来创立的。相反,FactoryBean
界说了一种创立 Bean 的办法,即运用 FactoryBean
的完结类的 getObject()
办法来创立 Bean。它能够让咱们在创立 Bean 的过程中进行愈加详尽的操控和定制。
Creating a bean that returns a specific object type
public class MyFactoryBean implements FactoryBean<String> {
@Override
public String getObject() throws Exception {
return "Hello, world!";
}
@Override
public Class<?> getObjectType() {
return String.class;
}
}
XML configuration
<bean id="myFactoryBean" class="com.example.MyFactoryBean"/>
This bean will always return the string “Hello, world!”. You can use this bean to create a constant value, for example.
这儿还有一种写法, 运用setter办法装备相关特色
装备文件
<bean id="myBeanFactory" class="com.hspedu.Spring.Factory.MyBeanFactory">
<property name="key" value="monster_01" />
</bean>
Java文件 MyBeanFactory
public class MyBeanFactory implements FactoryBean<Monster> {
private String key;
private Map<String, Monster> monster_map;
// 非静态代码块
{
monster_map = new HashMap<String, Monster>();
monster_map.put("monster_01", new Monster(100, "山公精", "吃人~~~"));
monster_map.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));
}
public void setKey(String key) {
this.key = key;
}
@Override
public Monster getObject() throws Exception {
return monster_map.get(key);
}
@Override
public Class<?> getObjectType() {
return Monster.class;
}
@Override
public boolean isSingleton() {
// 是否回来的是单例
return true;
}
}
调用函数, 获得对应的特色值
@Test
public void getBeanByBeanFactory() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
// 底层调用了 getObject() 办法
Monster bean = ioc.getBean("myBeanFactory", Monster.class);
System.out.println("bean = " + bean);
}
bean 装备信息重用(承继)
在 spring 的 ioc 容器, 供给了一种承继的办法来完结 bean 装备信息的重用
在 Spring 装备文件中,能够运用 Bean 装备信息重用(或称为承继)来减少冗余装备,进步装备文件的可读性,便利对系统进行保护。
具体来说,Bean 装备信息重用能够经过将多个 Bean 装备信息界说在一个通用的父 Bean 中,然后让多个子 Bean 承继这个父 Bean 的装备信息来完结。子 Bean 能够承继父 Bean 的特色值,结构函数、初始化办法等装备信息,而且还能够依据需求重载父 Bean 的某些装备信息,以到达更精细化的装备作用。
abstract=”true” 那么这个bean只能被于承继, 不能被实例化 子类也能够覆盖父类界说的子类特色的值
<!-- 装备monster方针
假如bean指定了 abstract="true" 那么只能被用于承继不能被实例化
-->
<bean id="monster10" class="com.hspedu.Spring.bean.Monster" abstract="true">
<property name="monsterId" value="10"/>
<property name="name" value="蜈蚣精~~"/>
<property name="skill" value="蜇人//"/>
</bean>
<!-- 1. 在装备一个monster 可是这个特色值和 monster01 相同
2. parent= "" 指定当时装备的特色值从 id="monster01"-->
<bean id="monster11" class="com.hspedu.Spring.bean.Monster"
parent="monster10" />
<bean id="monster12" class="com.hspedu.Spring.bean.Monster"
parent="monster10" />
bean 创立次序
- 在 spring 的 ioc 容器, 默许是按照装备的次序创立 bean 方针
<bean id="student01" class="com.hspedu.bean.Student" />
<bean id="department01" class="com.hspedu.bean.Department" />
- 会先创立 department01 方针,再创立 student01 方针.
<bean id="student01" class="com.hspedu.bean.Student"
设置了这个特色, 创立的循序就会发生变化
depends-on="department01"/><bean id="department01" class="com.hspedu.bean.Department" />
一个问题
- 先看下面的装备, 请问两个 bean 创立的次序是什么? 并剖析履行流程
- 先创立 id=memberDAOImpl
- 再创立 id = memberServiceImpl
- 调用 memberServiceImpl.setMemberDAO() 完结引证
- 先看下面的装备, 请问两个 bean 创立的次序是什么, 并剖析履行流程
- 先创立 id = memberServiceImpl
- 再创立 id=memberDAOImpl
-
- 用 memberServiceImpl.setMemberDAO() 完结引证
bean 方针的单例和多例
在 spring 的 ioc 容器, 在默许是按照单例创立的,即装备一个bean 方针后,ioc 容器只会创立一个 bean 实例。 假如,咱们期望 ioc 容器装备的某个 bean 方针,是以多个实例办法创立的则能够经过装备scope="prototype"
来指定
<!--
1. 这儿不写scope特色默许是单例的即只要一个方针
2. 假如scope="prototype" 那么便是每一次都创造一个新的方针
-->
<bean class="com.hspedu.Spring.bean.Cat" id="cat" scope="prototype">
<property name="name" value="小花猫"/>
<property name="age" value="12"/>
</bean>
细节
- 默许是单例 singleton, 在启动容器时, 默许就会创立 , 并放入到
singletonObjects
调集 - 当 设置为多实例机制后, 该bean 是在
getBean()
时才创立 - 如 果 是 单 例 singleton, 同 时 希 望 在 getBean 时 才创立, 能够指定懒加载
lazy-init="true"
(留意默许是 false) - 一般状况下, lazy-init 就运用默许值 false , 在开发看来, 用空间换时刻是值得的, 除非有特殊的要求.
- 假如
scope="prototype"
这时你的 lazy-init 特色的值不论是ture, 仍是false都是在getBean 时分,才创立方针.
bean 的生命周期
● 阐明: bean 方针创立是由 JVM 完结的,然后履行如下办法
- 履行结构器
- 履行 set 相关办法
- 调用 bean 的初始化的办法(需求装备) 能够
自界说姓名
- 运用 bean
- 当容器封闭时分,调用 bean 的毁掉办法(需求装备)
house 类
public class House {
private String name;
public House() {
System.out.println("House() 结构器");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("House setName()...");
this.name = name;
}
// 教师阐明, 这个创造是有程序员编写的
// 依据自己的事务逻辑
// 名子不是固定的
public void init() {
System.out.println("House init()..");
}
// 教师阐明, 这个创造是有程序员编写的
// 依据自己的事务逻辑
// 名子不是固定的
public void destory() {
System.out.println("House destory()..");
}
}
装备文件
- init-method= 指定bean初始化函数, 在getter办法之后
- destroy-method 指定bean毁掉时指定的办法
- init 和 destroy 办法指定的机遇由Spring容器指定
<!-- 装备方针, 演示整个bean生命周期 -->
<bean class="com.hspedu.Spring.bean.House" id="house"
init-method="init" destroy-method="destory">
<property name="name" value="北京豪宅" />
</bean>
测验文件
ConfigurableApplicationContext
接口 承继了 ApplicationContext
接口, 所以能够 ApplicationContext
的完结类能够向下转型, 一同由于close
办法在ConfigurableApplicationContext
这儿面才有
@Test
public void testBeanInitAndDes() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
House house = ioc.getBean("house", House.class);
System.out.println("house = " + house);
// 封闭容器
// ioc的编译类型是 ApplicationContext 运转类型是ClassPathXmlApplicationContext
// 由于ClassPathXmlApplicationContext 完结了 ConfigurableApplicationContext// 一同 ConfigurableApplicationContext 有close() 办法
// 而且 ConfigurableApplicationContext 承继 ApplicationContext 接口
((ConfigurableApplicationContext) ioc).close();
}
细节
- 初始化 init 办法和 destory 办法, 是程序员来指定
- 毁掉办法便是当封闭容器时,才会被调用, 直接退出不会调用
装备 bean 的后置处理器 【难点】
- 在 spring 的 ioc 容器,能够装备 bean 的后置处理器
- 该处理器/方针会在 bean 初始化办法调用前和初始化办法调用后被调用
- 程序员能够在后置处理器中编写自己的代码
界说一个后置处理器
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 在bean的init办法调用前履行
* @param bean 便是 ioc 容器回来的 bean 方针, 假如这儿被替换会修正,则回来的 bean 方针也会被修正
* @param beanName 便是 ioc 容器装备的 bean 的称号
* @return 程序员对传入的bean记忆回来/修正, 回来的 bean 方针
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization()~~" + "bean = "
+ bean + " beanName = " + beanName);
return bean;
}
/**
* 在bean的init办法调用后履行
* @param bean 便是 ioc 容器回来的 bean 方针, 假如这儿被替换会修正,则回来的 bean 方针也会被修正
* @param beanName 便是 ioc 容器装备的 bean 的称号
* @return 便是回来的 bean 方针
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization()~~");
return bean;
}
}
装备文件
<bean class="com.hspedu.Spring.bean.House" id="house"
init-method="init" destroy-method="destory">
<property name="name" value="大豪宅"/>
</bean>
<!-- 老韩解读
1. 当咱们在bean02.xml文件装备了MyBeanPostProcessor
2. 这时后置处理器就会作用在该容器创立的bean方针
3. 已经是针对一切方针编程 => 切面编程AOP
-->
<bean class="com.hspedu.Spring.bean.MyBeanPostProcessor" id="myBeanPostProcessor" />
小结
1、怎么履行到这个办法?=> 运用 AOP(反射+动态署理+IO+容器+注解)
2、有什么用?
=> 能够对 IOC 容器中一切的方针进行共同处理
,比方日志处理/权限的校验/安全的验证/事务办理
.
-初步体验事例: 假如类型是 House 的共同改成 上海豪宅
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 在bean的init办法调用前履行
*
* @param bean 便是 ioc 容器回来的 bean 方针, 假如这儿被替换会修正,则回来的 bean 方针也会被修正
* @param beanName 便是 ioc 容器装备的 bean 的称号
* @return 程序员对传入的bean记忆回来/修正, 回来的 bean 方针
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization()~~" + "bean = "
+ bean + " beanName = " + beanName);
if (bean instanceof House) {
((House) bean).setName("美国豪宅");
}
return bean;
}
/**
* 在bean的init办法调用后履行
*
* @param bean 便是 ioc 容器回来的 bean 方针, 假如这儿被替换会修正,则回来的 bean 方针也会被修正
* @param beanName 便是 ioc 容器装备的 bean 的称号
* @return 便是回来的 bean 方针
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization()~~" + "bean = "
+ bean + " beanName = " + beanName);
return bean;
}
}
3、针对容器的一切方针吗? 是的=>切面编程特色 4、后边咱们会自己完结这个底层机制,这个是一个比较难了解的知识点, 现在老韩不做过多的纠结,后边我会带小伙伴完结这个机制
经过特色文件给 bean 注入值
- 在 spring 的 ioc 容器,经过特色文件给 bean 注入值
bean装备文件
需求留意xmlns, 可能会报错!! 原因 : xmlns:context 中的url地址 xsi:schemaLocation 中没有相同的url 具体看看我的语雀文档 www.yuque.com/leikooo/wsk…
<?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">
<!--
指定特色文件
1. location="" 指定特色文件的方位, 需求带上classpath根目录下
2. 特色文件有中文的话, idea会主动帮你转化. 假如没有转化那么就在网站上转化完结就行
-->
<context:property-placeholder location="classpath:my.properties"/>
<!--
经过特色文件给monster赋值, 一同这时的特色值是经过 ${特色名} 来引证的
特色名便是 properties装备文件中的 key
-->
<bean class="com.hspedu.Spring.bean.Monster" id="monster">
<property name="monsterId" value="${monsterId}"/>
<property name="name" value="${name}"/>
<property name="skill" value="${skill}"/>
</bean>
</beans>
装备文件
monsterId=1000
name=jack
skill=hello
依据 XML 的 bean 的主动安装
byType
- 能够经过
autowire
完结主动安装 - autowire=”byType” 经过类型主动完结赋值/引证
- 比方OrderService 中有 OrderDAO 特色, 假如容器中假如过有OrderDAO 这个类型的方针就会主动安装
- 假如运用byType 那么不能有两个(或以上)这个类型的方针
- 假如没有没有特色那么 autowire没有必要写
<bean class="com.hspedu.Spring.DAO.OrderDAO" id="orderDAO"/>
<bean autowire="byType" class="com.hspedu.Spring.Service.OrderService" id="orderService" />
<bean autowire="byType" class="com.hspedu.Spring.web.OrderAction" id="orderAction" />
byName
- 假如咱们设置的是 autowrie=”byName” 表明经过姓名主动完结安装
- 例如 :
<bean autowire="byName" class="com.hspedu.Spring.Service.OrderService" id="orderService" />
- 先看OrderService的特色 private OrderDAO orderDAO
- 依据这个特色的 setXxx() 办法的 xxx来找方针的id
- public void setOrderDAO(OrderDAO orderDAO){…} 会依据id=orderDAO 方针来进行主动安装
- 假如没有就装备失败
演示
装备bean的xml文件
留意看, 这儿的id是 orderDAO2
<bean class="com.hspedu.Spring.DAO.OrderDAO" id="orderDAO2"/>
<bean autowire="byName" class="com.hspedu.Spring.Service.OrderService" id="orderService" />
<bean autowire="byName" class="com.hspedu.Spring.web.OrderAction" id="orderAction" />
</beans>
对应的Java文件
public class OrderService {
private OrderDAO orderDAO;
public OrderDAO getOrderDAO() {
return orderDAO;
}
// 留意看里是 setOrderDAO2
public void setOrderDAO2(OrderDAO orderDAO) {
this.orderDAO = orderDAO;
}
}
Spring EL 表达式 【了解】
- Spring Expression Language,Spring 表达式语言,简称 SpEL。支撑运转时查询并能够操作方针。
- 和 EL 表达式相同,SpEL 依据 JavaBean 风格的 getXxx()、setXxx()办法界说的特色拜访方针
- SpEL 运用#{…}作为定界符,一切在大框号中的字符都将被认为是SpEL 表达式。
- 不是重点,假如看到有人这样运用,能看懂即可
<?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 class="com.hspedu.Spring.bean.Monster" id="monster01">
<property name="monsterId" value="100"/>
<property name="name" value="牛魔王"/>
<property name="skill" value="芭蕉扇"/>
</bean>
<bean id="spELBean" class="com.hspedu.Spring.bean.SpELBean">
<!-- sp el 给字面量 也能够直接赋值 -->
<property name="name" value="#{'韩顺平教育'}"/>
<!-- sp el 引证其它 bean --><property name="monster" value="#{monster01}"/>
<!-- sp el 引证其它 bean 的特色值 -->
<property name="monsterName" value="#{monster01.name}"/>
<!-- sp el 调用一般办法 赋值 -->
<property name="crySound" value="#{spELBean.crySound()}"/>
<!-- sp el 调用静态办法 赋值 -->
<property name="bookName" value="#{T(com.hspedu.Spring.bean.SpELBean).read(' 天龙八部')}"/>
<!-- sp el 经过运算赋值 -->
<property name="result" value="#{89*1.2}"/>
</bean>
</beans>
依据注解装备 bean
● 基本介绍 依据注解的办法装备 bean, 主要是项目开发中的组件,比方Controller、Service、和DAO.
● 组件注解的办法有
- @Component 表明当时注解标识的是一个组件
- @Controller 表明当时注解标识的是一个操控器,一般用于Servlet
- @Service 表明当时注解标识的是一个处理事务逻辑的类,一般用于Service 类
- @Repository 表明当时注解标识的是一个持久化层的类,一般用于Dao 类
快速入门
@Component
public class MyComponent {
}
@Controller
public class UserAction {
}
@Repository
public class UserDAO {
}
@Service
public class UserService {
}
留意细节
- 需求导入 spring-aop-5.3.8.jar , 别忘了
- 必须在 Spring 装备文件中指定”主动扫描的包”,IOC 容器才能够检测到当时项目中哪些类被标识了注解, 留意到导入 context 称号空间
<!-- 装备主动扫描的包 -->
<context:component-scan base-package="com.hspedu.spring.component" />
能够运用通配符 * 来指定 ,比方 com.hspedu.spring.* 表明
–老韩发问: com.hspedu.spring.component
会不会去扫描它的子包? 答:会的
-
Spring 的 IOC 容器不能检测一个运用了
@Controller
注解的类到底是不是一个真正的操控器。注解的称号是用于程序员自己辨认当时标识的是什么组件。其它的@Service@Repository 也是相同的道理 【也便是说 spring 的 IOC 容器只要检查到注解就会生成方针,可是这个注解的意义 spring 不会辨认,注解是给程序员编程便利看的】 -
为什么是 .class 而不是 . Java。答 : 由于运转之后就切换到工作途径下去了, 即
out目录
表明只扫描满足要求的类.[运用的少,不想扫描,不写注解就能够, 知道这个知识点即可]
<context:component-scan base-package="com.hspedu.spring.component"resource-pattern="User*.class" />
- 扫除某些注解类
<!--
需求期望扫除某个包/及其子包下面的某种类型的注解
1. context:exclude-filter 运用这个标签
2. type 指定扫除的办法, 一般运用注解的办法
3. expression 要写某一个注解的全途径, 比方: org.springframework.stereotype.Service
-->
<context:component-scan base-package="com.hspedu.Spring.component">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
- 指定主动扫描哪些注解类
留意, 需求use-default-filters=”false” 这个必须指定
<!--
1. context:include-filter 这个表明要去扫描那些类
2. type="annotation" 按照注解的办法过滤/扫描
3. expression="org.springframework.stereotype.Controller" 指定扫描类型的全途径
-->
<context:component-scan base-package="com.hspedu.Spring.component" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
- 默许状况:标记注解后,类名首字母小写作为 id 的值。也能够运用注解的
value
特色指定id
值,而且 value 能够省掉 `
@Controller(value="userAction01")
@Controller("userAction01")
- 扩展-@Controller 、@Service、@Component 差异 : (回去看看一下教师的讲解的注解根底) zhuanlan.zhihu.com/p/454638478
自己完结注解办法
具体看完结代码
主动安装
依据注解装备 bean,也可完结主动安装,运用的注解是:@AutoWired 或许 @Resource
@AutoWired 的规矩阐明
- 在 IOC 容器中查找待安装的组件的类型,假如有唯一的bean 匹配,则运用该bean安装
- 如待安装的类型对应的 bean 在 IOC 容器中有多个,则运用待安装的
特色的特色名
作为 id 值再进行查找, 找到就安装,找不到就抛反常 - 先按类型, 再按姓名
UserAction
类
@Controller
public class UserAction {
@Autowired
private UserService userService200;
public void sayOk() {
System.out.println("UserAction sayOk()~");
userService200.hi();
System.out.println("UserAction 中的 userService的hash值是 = " + userService200);
}
}
UserService
类
@Service
public class UserService {
public void hi() {
System.out.println("UserService hi() ~~");
}
}
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 http://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="com.hspedu.Spring.component"/>
<bean id="userService200" class="com.hspedu.Spring.component.UserService" />
<bean id="userService300" class="com.hspedu.Spring.component.UserService" />
</beans>
测验办法
@Test
public void setPropertyAutowired() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans08.xml");
UserAction userAction = ioc.getBean("userAction", UserAction.class);
userAction.sayOk();
Object userService = ioc.getBean("userService200");
System.out.println("userService = " + userService);
System.out.println("userAction = " + userAction);
}
@Resource 的规矩阐明
-
@Resource
有两个特色是比较重要的,分是name
和type
, Spring 将@Resource注解的name 特色解析为 bean 的姓名,而 type 特色则解析为 bean 的类型.所以假如运用name特色,则运用 byName 的主动注入战略,而运用 type 特色时则运用byType 主动注入战略 - 假如@Resource 没有指定 name 和 type , 则
先运用byName
注入战略, 假如匹配不上,再运用 byType 战略
, 假如都不成功,就会报错 - 没有指定, 则先按姓名再按类型
@Controller
public class UserAction {
/*
1. @Resource(name = "userService") 表明安装的是 id=userService的方针
2. 运用type特色, 需求保证对应的类就一个
3. 假如不写的话, 先按 name 再按 type
*/
@Resource
private UserService userService200;
public void sayOk() {
System.out.println("UserAction sayOk()~");
userService200.hi();
System.out.println("UserAction 中的 userService的hash值是 = " + userService200);
}
}
细节
- 老韩主张,不论是@Autowired 仍是 @Resource 都保证特色名是标准的写法就能够注入.
- 如待安装的类型对应的 bean 在 IOC 容器中有多个,则运用待安装的特色的特色名作为 id 值再进行查找, 找到就安装,找不到就抛反常
- @AutoWired 能够配合注解 @Qualifier 进行指定安装的id
@Controller
public class UserAction {
// 者两个需求一同写
@Autowired
@Qualifier(value="userService200")
private UserService userService200;
public void sayOk() {
System.out.println("UserAction sayOk()~");
userService200.hi();
System.out.println("UserAction 中的 userService的hash值是 = " + userService200);
}
}
泛型依靠注入
- 只要让BasicService和BaseDao树立联系, 那么承继他们的泛型接口也会主动完结注入
- 传统办法是将 PhoneDao /BookDao 主动安装到 BookService/PhoneSerive 中,当这种承继联系多时,就比较费事,能够运用 spring 供给的泛型依靠注入
BaseService
类
@Service
public abstract class BaseService<T> {
@Autowired
private BaseDAO<T> baseDao;
public void save() {
baseDao.save();
}
}
BaseDAO
类
@Repository
public abstract class BaseDAO<T> {
public abstract void save();
}
}
完结后的作用
AOP
动态署理 【重要!!!!】
小事例入手
Vehicle
接口
public interface Vehicle {
void run();
String fly(int height);
}
ship
类 完结了Vehicle
接口
public class Ship implements Vehicle{
@Override
public void run() {
// System.out.println("交通工具开端运转了...");
System.out.println("大轮船在水上 running...");
// System.out.println("交通工具中止运转了...");
}
@Override
public String fly(int height) {
System.out.println("轮船在天上飞 = " + height);
return "轮船在天上飞 = " + height;
}
}
VehicleProxyProvider
署理类
public class VehicleProxyProvider {
// 界说一个特色
// target_vehicle 表明真正要履行的方针
// 该方针需求完结Vehicle接口
private Vehicle targetVehicle;
public VehicleProxyProvider(Vehicle targetVehicle) {
this.targetVehicle = targetVehicle;
}
// 编写一个办法, 回来一个署理方针
public Vehicle getProxy() {
/*
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
教师解读
1. Proxy.newProxyInstance() 能够回来一个署理方针
2. ClassLoader loader 类的加载器
3. Class<?>[] interfaces 便是将来署理类的接口信息
4. InvocationHandler h 调用出库去/方针 , 有一个非常重要的办法
*/
ClassLoader classLoader = targetVehicle.getClass().getClassLoader();
// 拿到方针的接口信息, 底层是经过接口来调用
Class<?>[] interfaces = targetVehicle.getClass().getInterfaces();
/*
InvocationHandler h 这个是一个接口, 不能直接方针, 所以需求 匿名内部类
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
*/
InvocationHandler h = new InvocationHandler() {
/**
* invoke 办法是将来履行咱们的 targetVehicle 的办法, 会调用
* @param proxy 代表署理方针
* @param method 便是经过署理方针调用办法 署理方针.run()
* @param args 表明调用署理方针调用办法的xx参数 署理方针.run(xx)
* @return 署理方针.run(xx) 回来的数据
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 这儿以后便是前置告诉
System.out.println("交通工具开端运转了...");
// 这儿反射加动态署理
Object invoke = method.invoke(targetVehicle, args);
System.out.println("交通工具中止运转了...");
return invoke;
}
};
Object instance = Proxy.newProxyInstance(classLoader, interfaces, h);
return (Vehicle) instance;
}
}
测验程序
@Test
public void proxyRun() throws Exception {
Vehicle ship = new Car();
// 创立了 vehicleProxyProvider 而且传入了要署理的方针ship
VehicleProxyProvider vehicleProxyProvider = new VehicleProxyProvider(ship);
// 获取署理方针
// proxy能够署理履行办法
// 编译类型是 Vehicle
// 运转类型是 署理类型 class com.sun.proxy.$Proxy4
// 当履行到run办法时会履行到署理方针的invoke
Vehicle proxy = vehicleProxyProvider.getProxy();
System.out.println("运转类型是 : " + proxy.getClass());
// 这个动态表现在许多不同的方面 1.方针 2. 办法
String fly = proxy.fly(100);
System.out.println("fly = " + fly);
}
下面是对getTargetVehicle()
办法中每一行代码的具体解说:
public Vehicle getTargetVehicle() {
// 获取方针车辆的类加载器
ClassLoader classLoader = targetVehicle.getClass().getClassLoader();
// 获取方针车辆完结的接口信息
Class<?>[] interfaces = targetVehicle.getClass().getInterfaces();
// 创立一个InvocationHandler方针
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
// 从AOP来看, 这个也是一个横切关注点
System.out.println("办法履行前 - 日志-办法名-" + method.getName() + "-参数 " + Arrays.asList(args));
result = method.invoke(smartAnimal, args);
System.out.println("办法履行正常完毕 - 日志-办法名-" + method.getName() + "-成果 result= " + result);
return result;
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
// 假如出现反常就会进入到catch {}
// 从AOP来看, 这个也是一个横切关注点
System.out.println("办法履行反常完毕 !!! --" + method.getName() + "--" + e.getClass().getName());
} finally {
// 不论有没有反常, 都会履行
// 从AOP来看, 这个也是一个横切关注点, 终究告诉
System.out.println("办法履行完毕 -- 日志 -- 办法" + method.getName());
}
};
// 运用类加载器、接口信息和InvocationHandler创立署理方针
Object instance = Proxy.newProxyInstance(classLoader, interfaces, h);
// 将署理方针转化为Vehicle类型并回来
return (Vehicle) instance;
}
解说每一行代码的作用:
-
ClassLoader classLoader = targetVehicle.getClass().getClassLoader();
:获取方针车辆方针的类加载器。类加载器用于加载和创立新的类实例。 -
Class<?>[] interfaces = targetVehicle.getClass().getInterfaces();
:获取方针车辆方针所完结的接口信息。这将用于创立署理方针,保证署理方针与方针车辆方针完结相同的接口。 -
InvocationHandler h = new InvocationHandler() { ... }
:创立一个匿名内部类作为InvocationHandler
接口的完结。InvocationHandler
接口用于界说署理方针的调用处理程序。 -
在匿名内部类的
invoke()
办法内部,咱们界说了办法调用前后的处理逻辑。在这个比方中,它会在调用方针车辆方针的办法之前打印”轮船开端运转~”,在办法调用后打印”轮船完毕运转~”。 -
Object instance = Proxy.newProxyInstance(classLoader, interfaces, h);
:运用类加载器、接口信息和InvocationHandler
创立署理方针。Proxy.newProxyInstance()
办法依据供给的参数创立一个署理方针,该署理方针将在办法调用时委托给InvocationHandler
的invoke()
办法进行处理。 -
return (Vehicle) instance;
:将署理方针转化为Vehicle
类型并回来。由于署理方针完结了Vehicle
接口,所以能够将其转化为Vehicle
类型,以便在代码其他部分运用。
经过这个署理方针,你能够在调用方针车辆方针的办法之前和之后履行额定的逻辑,例如打印日志、权限检查等。
AOP基本介绍
● AOP 完结办法
- 依据动态署理的办法[内置 aop 完结]
- 运用结构 aspectj 来完结 真正的SpringAOP!!
AOP快速入门
阐明
- 需求引进中心的 aspect 包
- 在切面类中声明告诉办法
- 前置告诉:@Before
- 回来告诉:@AfterReturning
- 反常告诉:@AfterThrowing
catch
{ } 里边 - 后置告诉:@After 在
finally
{ } 里边 - 盘绕告诉:@Around 将四个告诉兼并办理
切片类
@Component // 会注入到Spring容器
@Aspect // 表明是一个切面类 【底层切面编程的支撑(动态署理 + 反射 + 动态绑定 ~~)】
public class SmartAnimalAspect {
/**
* 期望将f1 切入到dog-getSum前履行
* 1. @Before 表明前置告诉 方针函数履行办法前履行
* 2. value = "execution(public int com.hspedu.Spring.AOP.aspectj.Dog.getSum(int ,int))" 表明那个类的那个办法
* 3. f1办法能够了解为一个切入办法, 办法名由程序猿指定 比方:showBeginLog
* 4. JoinPoint joinPoint 在底层履行时会主动传入joinPoint方针
*/
@Before(value = "execution(public int com.hspedu.Spring.AOP.aspectj.Dog.getSum(int ,int))")
public static void showBeginLog(JoinPoint joinPoint) {
// 拿到办法签名
Signature signature = joinPoint.getSignature();
System.out.println("办法履行前 - 日志 - 办法名- :" + signature.getName() + " -参数 " + Arrays.asList(joinPoint.getArgs()));
}
// 把f2切入到正常完毕之后的告诉
@AfterReturning(value = "execution(public int com.hspedu.Spring.AOP.aspectj.Dog.getSum(int ,int))")
public void showSuccessLog(JoinPoint joinPoint) {
System.out.println("办法履行正常完毕-日志-办法名: " + joinPoint.getSignature().getName());
}
@AfterThrowing(value = "execution(public int com.hspedu.Spring.AOP.aspectj.Dog.getSum(int ,int))")
public void showExceptionLog(JoinPoint joinPoint) {
System.out.println("办法履行反常-日志-办法名: " + joinPoint.getSignature().getName());
}
// 切入到办法履行之后 finally {}
@After(value = "execution(public int com.hspedu.Spring.AOP.aspectj.Dog.getSum(int ,int))")
public void showFinallyEndingLog(JoinPoint joinPoint) {
System.out.println("办法终究履行完毕 finally{} -日志-办法名: " + joinPoint.getSignature().getName());
}
}
测验办法
@Test
public void test1() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans10.xml");
// 这儿咱们经过接口类型来获取注入到Dog方针1
// 不能按照dog类型获取
// 在底层任然仍是dog方针, 可是当getBean()时就相当于 之前写的 getproxy() 办法, 而不只是单纯的拿到dog方针
SmartAnimal bean = ioc.getBean(SmartAnimal.class);
// System.out.println(bean.getClass());
int sum = bean.getSum(1, 2);
System.out.println("======");
int sub = bean.getSub(10, 3);
}
细节阐明
- 关于切面类办法命名能够自己标准一下, 比方 showBeginLog() . showSuccessEndLog()showExceptionLog(), showFinallyEndLog()
- 切入表达式的更多装备,比方运用
模糊装备
@Before(value="execution(* com.hspedu.aop.proxy.SmartDog.*(..))")
- 表明一切拜访权限,一切包的下一切有类的所办法,都会被履行该前置告诉办法
@Before(value="execution(* *.*(..))")
必需要敞开的设置
<!-- 敞开依据注解的 AOP 功用 -->
<aop:aspectj-autoproxy/>
- 当 spring 容器敞开了 , 咱们获取注入的方针, 需求以接口的类型来获取, 由于你注入的方针.getClass() 已经是署理类型了!
- 当 spring 容器敞开了 , 咱们获取注入的方针, 也能够经过 id 来获取, 可是也要转成接口类型.
AOP-切入表达式
- 切入表达式也能够指向类的办法, 这时切入表达式会对该类/方针收效
- 切入表达式也能够指向接口的办法, 这时切入表达式会对完结了接口的类/方针收效
- 切入表达式也能够对没有完结接口的类,进行切入
@Component // 把Car视为一个组件, 注入到容器之中
public class Car {
public void run() {
System.out.println("小汽车在running~~");
}
}
@Test
public void testHomeWork2() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans11.xml");
Car bean = ioc.getBean(Car.class);
bean.run();
// class com.hspedu.Spring.AOP.homework.Car$$EnhancerBySpringCGLIB$$e919e523
System.out.println("bean.getClass() = " + bean.getClass());
}
AOP-JoinPoint
JoinPoint 是指程序履行过程中能够被阻拦的特定点,例如办法的调用、办法的履行、反常的抛出等。
-
getArgs():获取办法参数数组。运用
joinPoint.getArgs()
能够获取被阻拦办法的参数数组。 -
getSignature():获取办法签名。运用
joinPoint.getSignature()
能够获取被阻拦办法的办法签名,包含办法名、回来类型等信息。 -
getTarget():获取方针方针。运用
joinPoint.getTarget()
能够获取被阻拦办法所属的方针方针。 -
getThis():获取署理方针。运用
joinPoint.getThis()
能够获取署理方针,即实际履行办法的方针。 -
proceed():持续履行办法。在盘绕告诉(Around Advice)中,运用
joinPoint.proceed()
能够持续履行被阻拦的办法。 -
getStaticPart():获取静态部分。运用
joinPoint.getStaticPart()
能够获取静态部分的信息,包含被阻拦办法的签名和参数。 -
getSourceLocation():获取源码方位。运用
joinPoint.getSourceLocation()
能够获取被阻拦办法在源码中的方位信息。 -
getModifiers() : 回来方针办法的修饰符号 回来的是数字 例如 : 假如一个办法具有
public static
修饰符,那么它的修饰符整数值便是 9(1(public) + 8(static))-
public
:1 -
private
:2 -
protected
:4 -
static
:8 -
final
:16 -
synchronized
:32 -
volatile
:64 -
transient
:128 -
native
:256 -
abstract
:1024 -
strictfp
:2048
-
public static void showBeginLog(JoinPoint joinPoint) {
// 拿到办法签名
Signature signature = joinPoint.getSignature();
System.out.println("办法履行前 - 日志 - 办法名- :" + signature.getName() + " -参数 " + Arrays.asList(joinPoint.getArgs()));
joinPoint.getSignature().getName(); // 获取方针办法名
joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取方针办法所属类的简略类名
joinPoint.getSignature().getDeclaringTypeName();// 获取方针办法所属类的类名
joinPoint.getSignature().getModifiers(); // 获取方针办法声明类型(public、private、protected)
Object[] args = joinPoint.getArgs(); // 获取传入方针办法的参数,回来一个数组
joinPoint.getTarget(); // 获取被署理的方针
joinPoint.getThis(); // 获取署理方针自己
}
AOP-回来告诉获取成果
需求 : 怎么在回来告诉办法获取回来成果
要得到成果需求在 @AfterReturning 这儿面获取, 其他的比方@Before那么就不行
/*
1. 假如咱们期望把方针办法履行的成果 ,回来切入办法
2. 能够再 @AfterReturning 添加特色, returning = "res"
3. 一同在切入办法 添加特色 Object res
4. 留意称号需求共同
*/
@AfterReturning(value = "execution(public int com.hspedu.Spring.AOP.aspectj.Dog.getSum(int ,int))", returning = "res")
public void showSuccessLog(JoinPoint joinPoint, Object res) {
// 方针办法的回来成果
System.out.println("回来的成果是: " + res);
System.out.println("办法履行正常完毕-日志-办法名: " + joinPoint.getSignature().getName());
}
AOP-反常告诉中获取反常
- 反常告诉办法中获取反常
在办法中运用 Throwable
接纳
@AfterThrowing(value = "execution(public int com.hspedu.Spring.AOP.aspectj.Dog.getSum(int ,int))", throwing = "mes")
public void showExceptionLog(JoinPoint joinPoint, Throwable mes) {
System.out.println("办法履行反常-日志-办法名: " + joinPoint.getSignature().getName());
// 反常是mes = java.lang.ArithmeticException: / by zero
System.out.println("反常是mes = " + mes);
}
AOP-盘绕告诉【了解】
- 盘绕告诉能够完结其它四个告诉要做的事情
留意
- 切入表达式形参需求是 ProceedingJoinPoint
- 需求 try-catch-finally
@Component
@Aspect
public class SmartAnimalAspect2 {
// 切入表达式, 这个便是能够替代前面4个注解
@Around(value = "execution(public int Dog.getSum(int ,int))")
public Object doAround(ProceedingJoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
Object result = null;
try {
System.out.println("AOP盘绕告诉 --" + name + "办法开端履行--形参有: " + Arrays.asList(args));
result = joinPoint.proceed();
System.out.println("AOP盘绕告诉 " + name + " 办法履行完毕 -- 成果是 " + result);
} catch (Throwable e) {
System.out.println("AOP盘绕告诉, 出现反常: " + e);
} finally {
System.out.println("AOP盘绕告诉终究告诉 ~~");
}
return result;
}
}
AOP-切入点表达式重用
● 切入点表达式重用
为了共同办理切入点表达式,能够运用切入点表达式重用技能。
留意
- @Before(value = “myPointCut()”) 需求写 “”
- @Pointcut 对应办法需求写的注解
@Component
@Aspect
public class UsbAspect {
@Pointcut(value = "execution(public int com.hspedu.Spring.AOP.homework.Phone.work())")
public void myPointCut() {
}
@Before(value = "myPointCut()")
public void showBeginLog(JoinPoint joinPoint) {
System.out.println("前置告诉-调用的办法名是 " + joinPoint.getSignature().getName());
}
@AfterReturning(value = "myPointCut()", returning = "res")
public void showSuccessLog(JoinPoint joinPoint, Object res) {
System.out.println("正常履行后-输出的办法名" + joinPoint.getSignature().getName() + "-回来值是 " + res);
}
@AfterThrowing(value = "myPointCut()", throwing = "mes")
public void showExceptionLog(JoinPoint joinPoint, Throwable mes) {
System.out.println("出现反常 办法名:" + joinPoint.getSignature().getName() + "反常是 " + mes);
}
}
AOP-切面优先级问题
- 假如同一个办法,有多个切面在同一个切入点切入,那么履行的优先级怎么操控.
- @order(value=n) 来操控 n 值越小,优先级越高.
- order 注解在 org.springframework.core.annotation.Order
履行次序
相似Filter
的过滤链式调用机制
@Aspect
@Order(1)
public class FirstAspect {
// 切面逻辑
}
@Aspect
@Order(2)
public class SecondAspect {
// 切面逻辑
}
AOP-依据 XML 装备 AOP
- 前面咱们是经过注解来装备 aop 的,在 spring 中,咱们也能够经过xml 的办法来装备AOP
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 依据xml, 完结aop编程-->
<bean class="com.hspedu.Spring.AOP.xml.Dog" id="dog"/>
<bean class="com.hspedu.Spring.AOP.xml.SmartAnimalAspect" id="animalAspect"/>
<!-- 必需要引进称号空间 xmlns:aop="http://www.springframework.org/schema/aop"-->
<aop:config>
<!-- 先装备切入点, 在装备切面方针-->
<aop:pointcut id="myPointCut" expression="execution(public int com.hspedu.Spring.AOP.xml.Dog.getSum(int , int))"/>
<!-- 这儿便是制定切面方针-->
<aop:aspect ref="animalAspect" order="10">
<!-- 装备前置告诉-->
<aop:before method="showBeginLog" pointcut-ref="myPointCut" />
<!-- 回来告诉-->
<aop:after-returning method="showSuccessLog" pointcut-ref="myPointCut" returning="res"/>
<!-- 反常告诉-->
<aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="mes"/>
<!-- 终究告诉-->
<aop:after method="showFinallyEndingLog" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>