总览

Spring5学习笔记
  1. Spring 中心学习内容 IOC、AOP, jdbcTemplate, 声明式事务
  2. IOC: 操控回转 , 能够办理 java 方针
  3. AOP : 切面编程
  4. JDBCTemplate : 是 spring 供给一套拜访数据库的技能, 应用性强,相对好了解
  5. 声明式事务: 依据 ioc/aop 完结事务办理, 了解有需求小伙伴花时刻
  6. IOC, AOP 是重点一同难点

Spring几个重要的概念

  1. Spring 能够整合其他的结构(老韩解读: Spring 是办理结构的结构)
  2. Spring 有两个中心的概念: IOC 和 AOP
  3. IOC [Inversion Of Control 回转操控]

● 传统的开发模式[JdbcUtils / 反射]

程序——>环境 程序读取环境装备,然后自己创立方针

Spring5学习笔记

● IOC 的开发模式 [EmpAction EmpService EmpDao Emp]

程序<—–容器 容器创立好方针,程序直接运用

Spring5学习笔记
  1. DI—Dependency Injection 依靠注入,能够了解成是 IOC 的另外叫法.
  2. Spring 最大的价值,经过装备,给程序供给需求运用的 web 层[Servlet(Action/Controller)]/Service/Dao/[JavaBean/entity]方针, 这个是中心价值所在,也是 ioc 的具体表现, 完结解耦
Spring5学习笔记

创立代码完结

ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

这儿读取的是out目录下的beans.xml文件

源码剖析

Spring5学习笔记
Spring5学习笔记
Spring5学习笔记
Spring5学习笔记
Spring5学习笔记
Spring5学习笔记
Spring5学习笔记

总结

Spring5学习笔记

快速入门

<?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包办理分为两个方面

  1. 创立bean方针
  2. 给bean注入特色

Bean的装备办法

  1. 依据xml文件装备办法
  2. 依据注解办法

依据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);  
}

细节

  1. 按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个, 否则会抛出反常NoUniqueBeanDefinitionException
  2. 这种办法的应用场景:比方 XxxAction/Servlet/Controller, 或XxxService 在一个线程中只需求一个方针实例(单例)的状况
  3. 教师这儿在阐明一下: 在容器装备文件(比方 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 方针的 彼此引证

  1. 这儿的ref便是依靠注入, spring底层帮你完结
  2. 留意在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一下底层没毛病

Spring5学习笔记

引证/注入内部 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中。具体完结办法如下:

  1. 数组类型的特色注入:
<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的字符串数组。

  1. 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的字符串列表。

  1. 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的字符串调集。

  1. 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的字符串键值对调集。

  1. 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);  
	}  
}

  1. 经过静态工厂获取
  2. class 是工厂的途径
  3. factory-method 表明的是指定工厂类是由哪一个方针回来
  4. constructor—-arg 的value指的是要指定回来哪一个工厂方针
<bean id="my_monster01"
	  class="com.hspedu.Spring.Factory.MyStaticFactory" 
	  factory-method="getMonster">  
	<constructor-arg value="1"/>  
</bean>

经过实例工厂获取方针

  1. 由于是非静态的所以需求造方针
  2. factory-bean 指定运用拿一个实例工厂
  3. factory-method 指定运用实例工厂的哪一个办法
  4. 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 创立次序

  1. 在 spring 的 ioc 容器, 默许是按照装备的次序创立 bean 方针
<bean id="student01" class="com.hspedu.bean.Student" />
<bean id="department01" class="com.hspedu.bean.Department" />
  1. 会先创立 department01 方针,再创立 student01 方针.
<bean id="student01" class="com.hspedu.bean.Student"
	  设置了这个特色, 创立的循序就会发生变化
	  depends-on="department01"/><bean id="department01" class="com.hspedu.bean.Department" />

一个问题

  1. 先看下面的装备, 请问两个 bean 创立的次序是什么? 并剖析履行流程
    1. 先创立 id=memberDAOImpl
    2. 再创立 id = memberServiceImpl
    3. 调用 memberServiceImpl.setMemberDAO() 完结引证
Spring5学习笔记
  1. 先看下面的装备, 请问两个 bean 创立的次序是什么, 并剖析履行流程
    1. 先创立 id = memberServiceImpl
    2. 再创立 id=memberDAOImpl
      1. 用 memberServiceImpl.setMemberDAO() 完结引证
Spring5学习笔记

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>

细节

  1. 默许是单例 singleton, 在启动容器时, 默许就会创立 , 并放入到singletonObjects 调集
  2. 当 设置为多实例机制后, 该bean 是在getBean()时才创立
  3. 如 果 是 单 例 singleton, 同 时 希 望 在 getBean 时 才创立, 能够指定懒加载lazy-init="true" (留意默许是 false)
  4. 一般状况下, lazy-init 就运用默许值 false , 在开发看来, 用空间换时刻是值得的, 除非有特殊的要求.
  5. 假如 scope="prototype" 这时你的 lazy-init 特色的值不论是ture, 仍是false都是在getBean 时分,才创立方针.

bean 的生命周期

● 阐明: bean 方针创立是由 JVM 完结的,然后履行如下办法

  1. 履行结构器
  2. 履行 set 相关办法
  3. 调用 bean 的初始化的办法(需求装备) 能够 自界说姓名
  4. 运用 bean
  5. 当容器封闭时分,调用 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()..");  
	}  
}

装备文件

  1. init-method= 指定bean初始化函数, 在getter办法之后
  2. destroy-method 指定bean毁掉时指定的办法
  3. init 和 destroy 办法指定的机遇由Spring容器指定
<!-- 装备方针, 演示整个bean生命周期  -->
<bean class="com.hspedu.Spring.bean.House" id="house"  
init-method="init" destroy-method="destory">  
	<property name="name" value="北京豪宅" />  
</bean>

测验文件

Spring5学习笔记

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();  
}

细节

  1. 初始化 init 办法和 destory 办法, 是程序员来指定
  2. 毁掉办法便是当封闭容器时,才会被调用, 直接退出不会调用

装备 bean 的后置处理器 【难点】

  1. 在 spring 的 ioc 容器,能够装备 bean 的后置处理器
  2. 该处理器/方针会在 bean 初始化办法调用前初始化办法调用后被调用
  3. 程序员能够在后置处理器中编写自己的代码

界说一个后置处理器

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
  1. 能够经过autowire 完结主动安装
  2. autowire=”byType” 经过类型主动完结赋值/引证
  3. 比方OrderService 中有 OrderDAO 特色, 假如容器中假如过有OrderDAO 这个类型的方针就会主动安装
  4. 假如运用byType 那么不能有两个(或以上)这个类型的方针
  5. 假如没有没有特色那么 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
  1. 假如咱们设置的是 autowrie=”byName” 表明经过姓名主动完结安装
  2. 例如 :
<bean autowire="byName" class="com.hspedu.Spring.Service.OrderService" id="orderService" />
  1. 先看OrderService的特色 private OrderDAO orderDAO
  2. 依据这个特色的 setXxx() 办法的 xxx来找方针的id
  3. public void setOrderDAO(OrderDAO orderDAO){…} 会依据id=orderDAO 方针来进行主动安装
  4. 假如没有就装备失败

演示

装备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 表达式 【了解】

  1. Spring Expression Language,Spring 表达式语言,简称 SpEL。支撑运转时查询并能够操作方针。
  2. 和 EL 表达式相同,SpEL 依据 JavaBean 风格的 getXxx()、setXxx()办法界说的特色拜访方针
  3. SpEL 运用#{…}作为定界符,一切在大框号中的字符都将被认为是SpEL 表达式。
  4. 不是重点,假如看到有人这样运用,能看懂即可
<?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.

● 组件注解的办法有

  1. @Component 表明当时注解标识的是一个组件
  2. @Controller 表明当时注解标识的是一个操控器,一般用于Servlet
  3. @Service 表明当时注解标识的是一个处理事务逻辑的类,一般用于Service 类
  4. @Repository 表明当时注解标识的是一个持久化层的类,一般用于Dao 类

快速入门

@Component
public class MyComponent {  
}
@Controller
public class UserAction {  
}
@Repository
public class UserDAO {  
}
@Service
public class UserService {  
}

留意细节

  1. 需求导入 spring-aop-5.3.8.jar , 别忘了
  2. 必须在 Spring 装备文件中指定”主动扫描的包”,IOC 容器才能够检测到当时项目中哪些类被标识了注解, 留意到导入 context 称号空间
<!-- 装备主动扫描的包 -->
<context:component-scan base-package="com.hspedu.spring.component" />

能够运用通配符 * 来指定 ,比方 com.hspedu.spring.* 表明

–老韩发问: com.hspedu.spring.component 会不会去扫描它的子包? 答:会的

  1. Spring 的 IOC 容器不能检测一个运用了@Controller 注解的类到底是不是一个真正的操控器。注解的称号是用于程序员自己辨认当时标识的是什么组件。其它的@Service@Repository 也是相同的道理 【也便是说 spring 的 IOC 容器只要检查到注解就会生成方针,可是这个注解的意义 spring 不会辨认,注解是给程序员编程便利看的】

  2. 为什么是 .class 而不是 . Java。答 : 由于运转之后就切换到工作途径下去了, 即out目录

表明只扫描满足要求的类.[运用的少,不想扫描,不写注解就能够, 知道这个知识点即可]
<context:component-scan base-package="com.hspedu.spring.component"resource-pattern="User*.class" />
  1. 扫除某些注解类
<!--
	需求期望扫除某个包/及其子包下面的某种类型的注解  
	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>
Spring5学习笔记
Spring5学习笔记
  1. 指定主动扫描哪些注解类

留意, 需求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>
  1. 默许状况:标记注解后,类名首字母小写作为 id 的值。也能够运用注解的value特色指定id 值,而且 value 能够省掉 `
@Controller(value="userAction01")
@Controller("userAction01")
  1. 扩展-@Controller 、@Service、@Component 差异 : (回去看看一下教师的讲解的注解根底) zhuanlan.zhihu.com/p/454638478

自己完结注解办法

Spring5学习笔记

具体看完结代码

主动安装

依据注解装备 bean,也可完结主动安装,运用的注解是:@AutoWired 或许 @Resource

@AutoWired 的规矩阐明
  1. 在 IOC 容器中查找待安装的组件的类型,假如有唯一的bean 匹配,则运用该bean安装
  2. 如待安装的类型对应的 bean 在 IOC 容器中有多个,则运用待安装的特色的特色名作为 id 值再进行查找, 找到就安装,找不到就抛反常
  3. 先按类型, 再按姓名

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 的规矩阐明
  1. @Resource 有两个特色是比较重要的,分是 nametype, Spring 将@Resource注解的name 特色解析为 bean 的姓名,而 type 特色则解析为 bean 的类型.所以假如运用name特色,则运用 byName 的主动注入战略,而运用 type 特色时则运用byType 主动注入战略
  2. 假如@Resource 没有指定 name 和 type , 则先运用byName注入战略, 假如匹配不上, 再运用 byType 战略, 假如都不成功,就会报错
  3. 没有指定, 则先按姓名再按类型
@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);  
	}  
}
细节
  1. 老韩主张,不论是@Autowired 仍是 @Resource 都保证特色名是标准的写法就能够注入.
  2. 如待安装的类型对应的 bean 在 IOC 容器中有多个,则运用待安装的特色的特色名作为 id 值再进行查找, 找到就安装,找不到就抛反常
  3. @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);  
	}  
}

泛型依靠注入

Spring5学习笔记
  • 只要让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();  
	}
}

完结后的作用

Spring5学习笔记

AOP

动态署理 【重要!!!!】

小事例入手

Spring5学习笔记

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;
}

解说每一行代码的作用:

  1. ClassLoader classLoader = targetVehicle.getClass().getClassLoader();:获取方针车辆方针的类加载器。类加载器用于加载和创立新的类实例。

  2. Class<?>[] interfaces = targetVehicle.getClass().getInterfaces();:获取方针车辆方针所完结的接口信息。这将用于创立署理方针,保证署理方针与方针车辆方针完结相同的接口。

  3. InvocationHandler h = new InvocationHandler() { ... }:创立一个匿名内部类作为InvocationHandler接口的完结。InvocationHandler接口用于界说署理方针的调用处理程序。

  4. 在匿名内部类的invoke()办法内部,咱们界说了办法调用前后的处理逻辑。在这个比方中,它会在调用方针车辆方针的办法之前打印”轮船开端运转~”,在办法调用后打印”轮船完毕运转~”。

  5. Object instance = Proxy.newProxyInstance(classLoader, interfaces, h);:运用类加载器、接口信息和InvocationHandler创立署理方针。Proxy.newProxyInstance()办法依据供给的参数创立一个署理方针,该署理方针将在办法调用时委托给InvocationHandlerinvoke()办法进行处理。

  6. return (Vehicle) instance;:将署理方针转化为Vehicle类型并回来。由于署理方针完结了Vehicle接口,所以能够将其转化为Vehicle类型,以便在代码其他部分运用。

经过这个署理方针,你能够在调用方针车辆方针的办法之前和之后履行额定的逻辑,例如打印日志、权限检查等。

AOP基本介绍

Spring5学习笔记
Spring5学习笔记
Spring5学习笔记

● AOP 完结办法

  1. 依据动态署理的办法[内置 aop 完结]
  2. 运用结构 aspectj 来完结 真正的SpringAOP!!

AOP快速入门

阐明

  1. 需求引进中心的 aspect 包
  2. 在切面类中声明告诉办法
    1. 前置告诉:@Before
    2. 回来告诉:@AfterReturning
    3. 反常告诉:@AfterThrowing catch{ } 里边
    4. 后置告诉:@After 在finally{ } 里边
    5. 盘绕告诉:@Around 将四个告诉兼并办理
Spring5学习笔记

切片类

@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);  
}

细节阐明

  1. 关于切面类办法命名能够自己标准一下, 比方 showBeginLog() . showSuccessEndLog()showExceptionLog(), showFinallyEndLog()
  2. 切入表达式的更多装备,比方运用模糊装备
@Before(value="execution(* com.hspedu.aop.proxy.SmartDog.*(..))")
  1. 表明一切拜访权限,一切包的下一切有类的所办法,都会被履行该前置告诉办法
@Before(value="execution(* *.*(..))")


必需要敞开的设置
<!-- 敞开依据注解的 AOP 功用 --> 
<aop:aspectj-autoproxy/>
  1. 当 spring 容器敞开了 , 咱们获取注入的方针, 需求以接口的类型来获取, 由于你注入的方针.getClass() 已经是署理类型了!
  2. 当 spring 容器敞开了 , 咱们获取注入的方针, 也能够经过 id 来获取, 可是也要转成接口类型.

AOP-切入表达式

Spring5学习笔记
Spring5学习笔记
  1. 切入表达式也能够指向类的办法, 这时切入表达式会对该类/方针收效
  2. 切入表达式也能够指向接口的办法, 这时切入表达式会对完结了接口的类/方针收效
  3. 切入表达式也能够对没有完结接口的类,进行切入
@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 是指程序履行过程中能够被阻拦的特定点,例如办法的调用、办法的履行、反常的抛出等。

  1. getArgs():获取办法参数数组。运用 joinPoint.getArgs() 能够获取被阻拦办法的参数数组。
  2. getSignature():获取办法签名。运用 joinPoint.getSignature() 能够获取被阻拦办法的办法签名,包含办法名、回来类型等信息。
  3. getTarget():获取方针方针。运用 joinPoint.getTarget() 能够获取被阻拦办法所属的方针方针。
  4. getThis():获取署理方针。运用 joinPoint.getThis() 能够获取署理方针,即实际履行办法的方针。
  5. proceed():持续履行办法。在盘绕告诉(Around Advice)中,运用 joinPoint.proceed() 能够持续履行被阻拦的办法。
  6. getStaticPart():获取静态部分。运用 joinPoint.getStaticPart() 能够获取静态部分的信息,包含被阻拦办法的签名和参数。
  7. getSourceLocation():获取源码方位。运用 joinPoint.getSourceLocation() 能够获取被阻拦办法在源码中的方位信息。
  8. 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-盘绕告诉【了解】

  • 盘绕告诉能够完结其它四个告诉要做的事情

留意

  1. 切入表达式形参需求是 ProceedingJoinPoint
  2. 需求 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-切入点表达式重用

● 切入点表达式重用

为了共同办理切入点表达式,能够运用切入点表达式重用技能。

留意

  1. @Before(value = “myPointCut()”) 需求写 “”
  2. @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 的过滤链式调用机制

Spring5学习笔记
@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>