Spring
Spring基本介绍
Spring 学习的核心内容

1、Spring 核心学习内容 IOC、AOP, jdbcTemplate, 声明式事务
2、IOC: 控制反转 , 可以管理 java 对象
3、AOP : 切面编程
4、DBCTemplate : 是 spring 提供一套访问数据库的技术, 应用性强,相对好理解
5、声明式事务: 基于 ioc/aop 实现事务管理, 理解有需要小伙伴花时间
6、IOC, AOP 是重点同时难点
Spring 几个重要概念
Spring 可以整合其他的框架(老韩解读: Spring 是管理框架的框架)
Spring 有两个核心的概念: IOC 和 AOP
IOC [Inversion Of Control 反转控制]
● 传统的开发模式[JdbcUtils / 反射]
程序------>环境 //程序读取环境配置,然后自己创建对象.

老韩解读上图(以连接到数据库为例说明)
1.程序员编写程序, 在程序中读取配置信息
创建对象, new Object???() // 反射方式
使用对象完成任务
● IOC 的开发模式 [EmpAction EmpService EmpDao Emp]
程序<-----容器 //容器创建好对象,程序直接使用.

老韩解读上图
1、Spring 根据配置文件 xml/注解, 创建对象, 并放入到容器(ConcurrentHashMap)中,
并且可以完成对象之间的依赖
2、当需要使用某个对象实例的时候, 就直接从容器中获取即可
3、程序员可以更加关注如何使用对象完成相应的业务, (以前是 new ... ==> 注解/配置
方式)
4、DI—Dependency Injection 依赖注入,可以理解成是 IOC 的另外叫法.
5、Spring 最大的价值,通过配置,给程序提供需要使用的
web 层[Servlet(Action/Controller)]/Service/Dao/[JavaBean/entity]对象,
这个是核心价值所在,也是 ioc 的具体体现, 实现解耦.
快速入门
package com.yb.bean;
public class Monster {
private Integer monsterId;
private String name;
private String skill;
public Monster(Integer monsterId, String name, String skill) {
this.monsterId = monsterId;
this.name = name;
this.skill = skill;
}
public Monster() {
}
public Integer getMonsterId() {
return monsterId;
}
public void setMonsterId(Integer monsterId) {
this.monsterId = monsterId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
@Override
public String toString() {
return "Monster{" +
"monsterId=" + monsterId +
", name='" + name + '\'' +
", skill='" + skill + '\'' +
'}';
}
}
<?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.yb.bean.Monster" id="monster01">
<property name="monsterId" value="100" />
<property name="name" value="牛魔王" />
<property name="skill" value="芭蕉扇" />
</bean>
</beans>
public class SpringBeanTest {
@Test
public void getMonster(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
// Object monster01 = ioc.getBean("monster01");
Monster monster01 = (Monster) ioc.getBean("monster01");
System.out.println(monster01+"运行内存"+monster01.getClass());
System.out.println(monster01.getMonsterId()+monster01.getName()+monster01.getSkill());
System.out.println("========================");
Monster moster02 = ioc.getBean("monster01", Monster.class);
System.out.println(moster02.getMonsterId()+moster02.getName()+moster02.getSkill());
}
@Test
public void classPath(){
File file = new File(this.getClass().getResource("/").getPath());
System.out.println(file);
}
}
debugger

spring 容器结构/机制

查看容器注入了哪些 bean 对象,会输出 bean 的 id
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}

作业
<bean class="com.yb.bean.Car" id="car1">
<property name="id" value="1"/>
<property name="name" value="比亚迪" />
<property name="price" value="20" />
</bean>
package com.yb.bean;
public class Car {
private Integer id;
private String name;
private Integer price;
public Car() {
}
public Car(Integer id, String name, Integer price) {
this.id = id;
this.name = name;
this.price = price;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
public class SpringBean1Test {
@Test
public void getCar(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans1.xml");
Car car1 = ioc.getBean("car1", Car.class);
System.out.println(car1);
}
}
Spring 管理 Bean-IOC
Spring 配置/管理 bean 介绍
Bean 管理包括两方面
1 创建 bean 对象
2 给 bean 注入属性
Bean 配置方式
1 基于 xml 文件配置方式
2 基于注解方式
基于 XML 配置 bean
通过类型来获取 bean
● 案例说明:
通过 spring 的 ioc 容器, 获取一个 bean 对象
说明:获取 bean 的方式:按类型
步骤
1.创建Monster.java
2.在beans.xml中配置
<bean class="com.yb.bean.Monster">
<property name="monsterId" value="1"/>
<property name="name" value="牛魔王"/>
<property name="skill" value="牛魔王拳"/>
</bean>
3.编写测试类
@Test
public void getMonsterByType(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster = ioc.getBean(Monster.class);
System.out.println("monster="+monster);
}
细节说明
- 按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个, 否则会抛出异常
NoUniqueBeanDefinitionException
- 这种方式的应用场景:比如 XxxAction/Servlet/Controller, 或 XxxService 在一个线程
中只需要一个对象实例(单例)的情况
- 老师这里在说明一下: 在容器配置文件(比如 beans.xml)中给属性赋值, 底层是通过
setter 方法完成的, 这也是为什么我们需要提供 setter 方法的原因
通过构造器配置 bean
● 案例说明:
在 spring 的 ioc 容器, 可以通过构造器来配置 bean 对象
步骤
1.创建Monster.java
2.beans.xml中配置
通过索引
<bean id="monster03" class="com.hspedu.spring.beans.Monster">
<constructor-arg value="2" index="0"/>
<constructor-arg value="蜘蛛精" index="1"/>
<constructor-arg value="吐口水" index="2"/>
</bean>
通过type
<bean id="monster04" class="com.hspedu.spring.beans.Monster">
<constructor-arg value="3" type="java.lang.Integer"/>
<constructor-arg value="白骨精" type="java.lang.String"/>
<constructor-arg value="白骨鞭" type="java.lang.String"/>
</bean>
通过name
<bean id="monster05" class="com.hspedu.spring.beans.Monster">
<constructor-arg value="3" name="monsterId"/>
<constructor-arg value="白骨精" name="name"/>
<constructor-arg value="白骨鞭" name="skill"/>
</bean>
3.编写测试类
public class SpringBean1Test {
@Test
public void setBeanByConstructor(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster03 = ioc.getBean("monster03", Monster.class);
System.out.println(monster03 );
}
}
使用细节
通过 index 属性来区分是第几个参数
通过 type 属性来区分是什么类型(按照顺序)
通过 p 名称空间配置 bean
● 案例说明:
在 spring 的 ioc 容器, 可以通过 p 名称空间来配置 bean 对象
步骤
1.创建Monster.java
2.beans.xml配置
<bean id="monster06" class="com.hspedu.spring.beans.Monster"
p:monsterId="4"
p:name="红孩儿"
p:skill="吐火~"
/>
3.编写测试类
public class SpringBean1Test {
@Test
public void setBeanByP(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster06 = ioc.getBean("monster06", Monster.class);
System.out.println(monster06);
}
}
引用/注入其它 bean 对象
● 案例说明:
在 spring 的 ioc 容器, 可以通过 ref 来实现 bean 对象的相互引用
步骤
1 创建dao\MemberDAOImpl.java
public class MemberDAOImpl {
public MemberDAOImpl() {
System.out.println("MemberDAOImpl 构造器...");
}
public void add() {
System.out.println("MemberDAOImpl add()方法");
}
}
2 创建service\MemberServiceImpl.java
package com.yb.bean.service;
import com.yb.bean.dao.MemberDAOImpl;
public class MemberServiceImpl {
private MemberDAOImpl memberDAO;
public MemberServiceImpl() {
System.out.println("MemberServiceImpl 构造器~");
}
public void add() {
System.out.println("MemberServiceImpl add()...");
memberDAO.add();
}
public void setMemberDAO(MemberDAOImpl memberDAO) {
this.memberDAO = memberDAO;
}
public MemberDAOImpl getMemberDAO() {
return memberDAO;
}
}
3 创建beans.xml
<bean id="memberDAOImpl" class="com.yb.bean.dao.MemberDAOImpl"/>
<bean id="memberService" class="com.yb.bean.service.MemberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
4 编写测试类
@Test
public void getMonsterByRef(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
MemberServiceImpl memberService = ioc.getBean("memberService", MemberServiceImpl.class);
memberService.add();
}
在 beans.xml 配置
● 案例说明
在 spring 的 ioc 容器, 可以直接配置内部 bean 对象
步骤
1 创建dao\MemberDAOImpl.java
public class MemberDAOImpl {
public MemberDAOImpl() {
System.out.println("MemberDAOImpl 构造器...");
}
public void add() {
System.out.println("MemberDAOImpl add()方法");
}
}
2 创建service\MemberServiceImpl.java
package com.yb.bean.service;
import com.yb.bean.dao.MemberDAOImpl;
public class MemberServiceImpl {
private MemberDAOImpl memberDAO;
public MemberServiceImpl() {
System.out.println("MemberServiceImpl 构造器~");
}
public void add() {
System.out.println("MemberServiceImpl add()...");
memberDAO.add();
}
public void setMemberDAO(MemberDAOImpl memberDAO) {
this.memberDAO = memberDAO;
}
public MemberDAOImpl getMemberDAO() {
return memberDAO;
}
}
3 创建beans.xml
<bean id="memberService02" class="com.yb.bean.service.MemberServiceImpl">
<property name="memberDAO">
<bean class="com.yb.bean.dao.MemberDAOImpl"/>
</property>
</bean>
4 编写测试类
@Test
public void getMonsterByRef(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
MemberServiceImpl memberService02= ioc.getBean("memberService02", MemberServiceImpl.class);
memberService02.add();
}
引用/注入集合/数组类型
● 案例说明
在 spring 的 ioc 容器, 看看如何给 bean 对象的集合/数组类型属性赋值
步骤
创建 Monster.java, 前面有了
2.创建beans\Master.java
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 都是 String
private Properties pros;
//get set toString
3 . 配置beans.xml
<!-- 配置Master对象-->
<bean id="master" class="com.yb.bean.Master">
<property name="monsterList">
<list>
<ref bean="monster01"/>
<ref bean="monster02"/>
<!-- 或者嵌套内部bean-->
<bean class="com.yb.bean.Monster" id="monster03">
<property name="monsterId" value="1"/>
<property name="name" value="牛魔王"/>
<property name="skill" value="牛魔王拳"/>
</bean>
</list>
</property>
<property name="monsterMap">
<map>
<entry>
<key>
<value>monster001</value>
</key>
<ref bean="monster03"/>
</entry>
</map>
</property>
<property name="monsterSet">
<set>
<ref bean="monster01"/>
<bean class="com.yb.bean.Monster">
<property name="monsterId" value="10"/>
<property name="name" value="玉兔精"/>
<property name="skill" value="钻地洞"/>
</bean>
</set>
</property>
<!-- 给 bean 对象的数组属性注入值 -->
<property name="monsterName">
<array>
<value>银角大王</value>
<value>金角大王</value>
</array>
</property>
<property name="pros">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
<prop key="ip">127.0.0.1</prop>
</props>
</property>
</bean>
4 测试
public class SpringBean1Test {
@Test
public void setBeanByCollection() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Master master = ioc.getBean("master", Master.class);
System.out.println(master);
}
}
使用细节
主要掌握 List/Map/Properties 三种集合的使用.
Properties 集合的特点
这个 Properties 是 Hashtable 的子类 , 是 key-value 的形式
key 是 string 而 value 也是 string
通过 util 名称空间创建 list
● 案例说明
spring 的 ioc 容器, 可以通过 util 名称空间创建 list 集合
步骤
1 创建BookStore.java
package com.yb.bean;
import java.util.List;
public class BookStore {//书店
private List<String> bookList;
public BookStore() {
}
public List<String> getBookList() {
return bookList;
}
public void setBookList(List<String> bookList) {
this.bookList = bookList;
}
}
2 配置beans.xml文件
<util:list id="myListBook">
<value>三国演义</value>
<value>西游记</value>
<value>红楼梦</value>
<value>水浒传</value>
</util:list>
<bean id="bookStore" class="com.yb.bean.BookStore">
<property name="bookList" ref="myListBook"/>
</bean>
3 测试类
@Test
public void getListByUtil() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
BookStore bookStore = ioc.getBean("bookStore", BookStore.class);
List<String> bookList = bookStore.getBookList();
for (String s : bookList) {
System.out.println(s);
}
}
级联属性赋值
● 案例说明
spring 的 ioc 容器, 可以直接给对象属性的属性赋值, 即级联属性赋值
1 创建 Dept.java
public class Dept {
private String name;
public Dept() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2 创建Emp.java
package com.yb.bean;
public class Emp {
private String name;
private Dept dept;
public Emp() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
3 配置beans.xml 文件
<bean id="dept" class="com.yb.bean.Dept" />
<bean id="emp" class="com.yb.bean.Emp">
<property name="name" value="jack" />
<property name="dept" ref="dept" />
<property name="dept.name" value="财务部" />
</bean>
4 测试类
@Test
public void getListByRelation() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Emp emp = ioc.getBean("emp", Emp.class);
System.out.println(emp);
}
通过静态工厂获取对象
● 案例说明
在 spring 的 ioc 容器, 可以通过静态工厂获取 bean 对象
步骤
1 创建factory\MyStaticFactory.java
public class MyStaticFactory {
private static Map<String, Monster> monsterMap;
static {
monsterMap = new HashMap<String, Monster>();
monsterMap.put("monster_01", new Monster(100, "黄袍怪", "一阳指"));
monsterMap.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));
}
public static Monster getMonster(String key) {
return monsterMap.get(key);
}
}
2 配置beans.xml
<bean class="com.yb.bean.factory.MyStaticFactory"
id="my_monster01"
factory-method="getMonster"
>
<constructor-arg value="monster_02"/>
</bean>
3 测试
@Test
public void getListByStaticFactory() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster myMonster01 = ioc.getBean("my_monster01", Monster.class);
System.out.println(myMonster01);
}
通过实例工厂获取对象
● 案例说明
在 spring 的 ioc 容器, 可以通过实例工厂获取 bean 对象
步骤
1 创建\MyInstanceFactory.java
package com.yb.bean.factory;
import com.yb.bean.Monster;
import java.util.HashMap;
import java.util.Map;
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);
}
}
2 创建beans.xml
<bean id="myInstanceFactory" class="com.yb.bean.factory.MyInstanceFactory"/>
<!-- 1.factory-bean 指定使用哪个处例工厂对象返回bean-->
<!-- 2.factory-method 指定使用实例工厂对象的哪个方法返回bean-->
<bean id="my_monster2" factory-bean="myInstanceFactory"
factory-method="getMonster">
<constructor-arg value="monster_02"/>
</bean>
3 测试类
@Test
public void getListByInstanceFactory() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster myMonster2 = ioc.getBean("my_monster2", Monster.class);
System.out.println(myMonster2);
}
通过 FactoryBean 获取对象(重点)
● 案例说明
在 spring 的 ioc 容器,通过 FactoryBean 获取 bean 对象(重点)
步骤
1 创建**factory**MyFactoryBean.java
public class MyFactoryBean 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 {
// TODO Auto-generated method stub
return this.monster_map.get(key);
}
@Override
public Class getObjectType() {
// TODO Auto-generated method stub
return Monster.class;
}
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return true;
}
}
2 配置beans.xml
<bean id="my_monster03" class="com.yb.bean.factory.MyFactoryBean">
<property name="key" value="monster_01" />
</bean>
3 测试
@Test
public void getListByFactoryBean() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster myMonster03 = ioc.getBean("my_monster03", Monster.class);
System.out.println(myMonster03);
}
bean 配置信息重用(继承)
● 说明
在 spring 的 ioc 容器, 提供了一种继承的方式来实现 bean 配置信息的重用
步骤
1 配置beans.xml
<bean id="monster10" class="com.yb.bean.Monster">
<property name="monsterId" value="10"/>
<property name="name" value="蜈蚣精"/>
<property name="skill" value="蜇人"/>
</bean>
<bean id="monster11" class="com.yb.bean.Monster" parent="monster10" />
2 测试
@Test
public void getListByExtends() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster11 = ioc.getBean("monster11", Monster.class);
System.out.println(monster11);
}
bean 创建顺序
说明
- 在 spring 的 ioc 容器, 默认是按照配置的顺序创建 bean 对象
会先创建 student01 这个 bean 对象,然后创建 department01 这个 bean 对象
- 如果这样配置
会先创建 department01 对象,再创建 student01 对象**.**
小问题
先创建 id=memberDAOImpl
再创建 id = memberService
调用 memberServiceImpl.setMemberDAO() 完成引用
<bean id="memberDAOImpl" class="com.yb.bean.dao.MemberDAOImpl"/>
<bean id="memberService" class="com.yb.bean.service.MemberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
先创建 id = memberService
再创建 id=memberDAOImpl
调用 memberServiceImpl.setMemberDAO() 完成引用
<bean id="memberService" class="com.yb.bean.service.MemberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
<bean id="memberDAOImpl" class="com.yb.bean.dao.MemberDAOImpl"/>
bean 对象的单例和多例
● 说明
在 spring 的 ioc 容器, 在默认是按照单例创建的,即配置一个 bean 对象后,ioc 容器只会
创建一个 bean 实例。
如果,我们希望 ioc 容器配置的某个 bean 对象,是以多个实例形式创建的则可以通过配置
scope="prototype" 来指定
使用细节
默认是单例 singleton, 在启动容器时, 默认就会创建 , 并放入到 singletonObjects 集合
当
<bean scope="prototype" >
设置为多实例机制后, 该 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 的销毁方法(需要配置)
package com.yb.bean;
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()..");
}
}
配置 beans.xml
<bean id="house" class="com.hspedu.spring.beans.House"
init-method="init" destroy-method="destory">
<property name="name" value="北京豪宅"/>
</bean>
使用细节
初始化 init 方法和 destory 方法, 是程序员来指定
销毁方法就是当关闭容器时,才会被调用
配置 bean 的后置处理器 【这个比较难】
● 说明
在 spring 的 ioc 容器,可以配置 bean 的后置处理器
该处理器/对象会在 bean 初始化方法调用前和初始化方法调用后被调用
程序员可以在后置处理器中编写自己的代码
package com.yb.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 在 bean 初始化之前完成某些任务
*
* @param bean : 就是 ioc 容器返回的 bean 对象, 如果这里被替换会修改,则返
* 回的 bean 对象也会被修改
* @param beanName: 就是 ioc 容器配置的 bean 的名称
* @return Object: 就是返回的 bean 对象
*/
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization 被 调 用 " + beanName + " bean= " + bean.getClass());
return bean;
}
/**
* 在 bean 初始化之后完成某些任务
*
* @param bean : 就是 ioc 容器返回的 bean 对象, 如果这里被替换会修改,则返
* 回的 bean 对象也会被修改
* @param beanName: 就是 ioc 容器配置的 bean 的名称
* @return Object: 就是返回的 bean 对象
*/
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("postProcessAfterInitialization 被调用 " + beanName + " bean= " + bean.getClass());
return bean;
}
}
配置beans.xml
<!-- 配置 bean 的初始化方法和销毁方法 -->
<bean id="house" class="com.yb.bean.House"
init-method="init" destroy-method="destory">
<property name="name" value="北京豪宅"/>
</bean>
<!-- bean 后置处理器的配置 -->
<bean id="myBeanPostProcessor" class="com.yb.bean.MyBeanPostProcessor"/>
测试
@Test
public void testBeanPostProcessor() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans1.xml");
House house = ioc.getBean("house", House.class);
System.out.println(house);
//关闭容器
((ConfigurableApplicationContext) ioc).close();
}
通过属性文件给 bean 注入值
● 说明
在 spring 的 ioc 容器,通过属性文件给 bean 注入值
1 创建 my.properties
monsterId=1001
name=jack
skill=hello
中文需要unicode转码
2 配置beans.xml
<context:property-placeholder location="classpath:my.properties" />
<bean id="monster110" class="com.yb.bean.Monster">
<property name="monsterId" value="${monsterId}" />
<property name="name" value="${name}" />
<property name="skill" value="${skill}" />
</bean>
3 测试类
@Test
public void testBeanPostProcessor() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans3.xml");
Monster monster110 = ioc.getBean("monster110", Monster.class);
System.out.println(monster110);
}
基于 XML 的 bean 的自动装配
● 说明
在 spring 的 ioc 容器,可以实现自动装配 bean
步骤
1 创建 \dao\OrderDao.java
service\OrderService.java
**action**OrderAction.java
package com.yb.bean.dao;
public class OrderDao {
public void saveOrder() {
System.out.println("保存 一个订单...");
}
}
package com.yb.bean.service;
import com.yb.bean.dao.OrderDao;
public class OrderService {
private OrderDao orderDao;
public OrderDao getOrderDao() {
return orderDao;
}
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
}
package com.yb.bean.web;
import com.yb.bean.service.OrderService;
public class OrderAction {
private OrderService orderService;
public OrderService getOrderService() {
return orderService;
}
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
}
2 配置beans.xml
老师解读
1.autowire="byType"表示在创建orderService的通过类型的方式给对象属性自动完成赋值/引用
2比如0rderService 对象有private 0rderDao orderao
3就会在容器中去找有没有 0rderDao类型对象
4如果有,就会自动的装配,老师提示如果是按照byType方式来装配,这个容器中,不能有两个相同的0rderDao类型对象
5.如果你的对象没有属性autowire就没有必要写了
1)先看0rderService 属性private 0rderDao orderDao
2再根据这个属性的setXxx()方法的xxx来对象id
3)public void setOrderDao()就会找id=orderDao对象来进行自动装配
47如果没有就装配失败
<bean class="com.yb.bean.dao.OrderDao" id="orderDao" />
<bean autowire="byType" class="com.yb.bean.service.OrderService" id="orderService" />
<bean autowire="byType" class="com.yb.bean.web.OrderAction" id="orderAction" />
测试类
@Test
public void setBeanAutowire() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans3.xml");
OrderAction orderAction = ioc.getBean("orderAction", OrderAction.class);
System.out.println(orderAction.getOrderService().getOrderDao());
}
基于注解配置 bean
● 基本介绍
基于注解的方式配置 bean, 主要是项目开发中的组件,比如 Controller、Service、和 Dao.
● 组件注解的形式有
- @ComponentScan:用于自动扫描并注册 Spring 组件,例如带有 @Component、@Service、@Repository 或 @Controller 注解的类。
- @Component:标识一个类作为 Spring 组件,由 Spring 运行时进行管理。
- @Controller:用于标识控制器层(Controller Layer)中的类,控制器层接收并处理来自客户端的请求,调用适当的服务层方法处理请求,并返回响应。
- @Service:通常用于标识服务层(Service Layer)中的类,服务层负责处理业务逻辑,与数据访问和控制层进行交互,它们通常包含了应用程序的核心业务逻辑。
- @Repository:用于标识数据访问层(Repository Layer)中的类,数据访问层主要负责与数据库或其他数据存储交互,执行数据操作,并且封装了数据访问细节。
- @Autowired:用于自动装配 Spring Bean,可以在构造函数、属性或方法上使用,Spring 将根据类型自动注入 Bean
- @Qualifier:与 @Autowired 一起使用,指定要注入的 Bean 的名称,用于解决自动装配时的歧义性。
- @Configuration:指示一个类包含 Spring Bean 配置信息,通常与 @Bean 注解一起使用,允许使用 Java 类来定义 Spring Bean。
- @Bean:在 @Configuration 类中使用,用于定义 Spring Bean。
- @Scope:用于指定 Bean 的作用域,例如 Singleton、Prototype 等
快速入门
1 创建 UserAction.javaUserService.java, UserDao.javaMyComponent.java
package com.yb.component;
import org.springframework.stereotype.Controller;
@Controller
public class UserAction {
}
package com.yb.component;
import org.springframework.stereotype.Service;
@Service
public class UserService {
}
package com.yb.component;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}
package com.yb.component;
import org.springframework.stereotype.Component;
@Component
public class MyComponent {
}
2 配置beans.xml
<context:component-scan base-package="com.yb.component"/>
3 测试类
@Test
public void setBeanAutowire() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans4.xml");
MyComponent myComponent = ioc.getBean(MyComponent.class);
UserAction userAction = ioc.getBean(UserAction.class);
UserDao userDao = ioc.getBean(UserDao.class);
UserService userService = ioc.getBean(UserService.class);
System.out.println(myComponent);
System.out.println(userAction);
System.out.println(userDao);
System.out.println(userService);
}
注意事项和细节说明
1. 必须在 Spring 配置文件中指定"自动扫描的包",IOC 容器才能够检测到当前项目中哪些类被标识了注解, 注意到导入 context 名称空间
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.hspedu.spring.component" />
可以使用通配符 来指定 ,比如 com.hspedu.spring. 表示
--老韩提问: com.hspedu.spring.component 会不会去扫描它的子包?
答:会的
3. Spring 的 IOC 容器不能检测一个使用了@Controller 注解的类到底是不是一个真正的控
制器。注解的名称是用于程序员自己识别当前标识的是什么组件。其它的@Service
@Repository 也是一样的道理 [也就是说 spring 的 IOC 容器只要检查到注解就会生成对象,
但是这个注解的含义 spring 不会识别,注解是给程序员编程方便看的]
4.<context:component-scan
base-package="com.hspedu.spring.component"
resource-pattern="User*.class" />resource-pattern="User*.class": 表示只扫描满足要求的
类.[使用的少,不想扫描,不写注解就可以, 知道这个知识点即可]
5. <context:component-scan base-package="com.hspedu.spring.component" >
<!-- 排除哪些类 , 以 annotaion 注解为例 -->
<context:exclude-filter
type="annotation"
expression="org.springframework.stereotype.Service"/>
</context>
6. 指定自动扫描哪些注解类
<!--
老韩解读
1. use-default-filters="false": 不再使用默认的过滤机制
2. context:include-filter: 表示只是扫描指定的注解的类
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>
7. 默认情况:标记注解后,类名首字母小写作为 id 的值。也可以使用注解的 value 属性
指定 id 值,并且 value 可以省略。
@Controller(value="userAction01")
@Controller("userAction01")
手动开发Spring 基于注解配置的程序
1. 自 己 写 一 个 简 单 的 Spring 容 器 , 通 过 读 取 类 的 注 解 (@Component @Controller**
@Service @Reponsitory),将对象注入到 IOC 容器****
2. 也就是说,不使用 Spring 原生框架,我们自己使用 IO+Annotaion+反射+集合 技术实
现, 打通 Spring 注解方式开发的技术痛点****

步骤
1创建注解 ComponentScan.java
/**
* 定义我们的 ComponentScan 注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value();
}
2 创建 HspSpringConfig.java
/**
韩顺平 Java 工程师
* @author 韩顺平
* @version 1.0
* 作用类似我们的 beans.xml 文件, 用于对 spring 容器指定配置信息
*/
//指定要扫描的包
@ComponentScan("com.hspedu.spring.component")
public class HspSpringConfig {
}
3 创建HspSpringApplicationContext.java
/**
* @author 韩顺平
* @version 1.0
* 充当容器类,类似 Spring 原生的 ApplicationContext
*/
public class HspSpringApplicationContext {
private Class configClass;
private final ConcurrentHashMap<String, Object> ioc = new
ConcurrentHashMap<>();
public ConcurrentHashMap<String, Object> getIoc() {
return ioc;
}
public HspSpringApplicationContext(Class configClass) {
this.configClass = configClass;
//1. 解析配置类
//2. 获取到配置类的 @ComponentScan("com.hspedu.spring.component")
ComponentScan componentScan = (ComponentScan)
this.configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
System.out.println("扫描路径 = " + path);
//3. 获取扫描路径下所有的类文件
//(1) 先得到类加载器, 使用 App 方式来加载.
ClassLoader classLoader = HspSpringApplicationContext.class.getClassLoader();
//在获取某个包的对应的 URL 时,要求是 com/hspedu/spring/component
//URL resource = classLoader.getResource("com/hspedu/spring/component");
//(2) 将 path 转成 形式为 com/hspedu/spring/component
path = path.replace(".", "/");
//(3) 这 里 是 获 取 到 我 们 要 加 载 的 资 源 ( 类 ) 的 路 径 ( 工 作 路 径 :file:/D:/hspedu_ssm_temp/spring5/out/production/spring5/com/hspedu/spring/component)
// , 目的是为了扫描该路径下的类
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
System.out.println("=======================================");
System.out.println("文件绝对路径 = " + fileAbsolutePath);
if (fileAbsolutePath.endsWith(".class")) {//说明是类文件
//通过类加载器获取来类文件的 Clazz 对象
// 先 得 到 类 的 完 整 类 路 径 形 式 为com.hspedu.spring.component.MonsterService
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
fileAbsolutePath.indexOf(".class"));
String classFullPath = path.replace("/", ".") + "." + className;
System.out.println("类名 = " + className);
System.out.println("类的全路径 = " + classFullPath);
try {
//获取到扫描包下的类的 clazz 对象
Class<?> clazz = classLoader.loadClass(classFullPath);
if (clazz.isAnnotationPresent(Component.class) ||
clazz.isAnnotationPresent(Controller.class) ||
clazz.isAnnotationPresent(Service.class) ||
clazz.isAnnotationPresent(Repository.class)) {
//老师这里只是测试一个, 对Component, 取出指定的值
// if(clazz.isAnnotationPresent(Component.class)) {
// Component annotation =clazz.getAnnotation(Component.class);
// if(!"".equals(annotation.value())) {
// className = annotation.value();
// }
// }
//如果这个类有@Commponent, 说明是一个 springbean
System.out.println("是一个 bean = " + clazz);
//将其反射生成到 ioc 中.
Class<?> aClass = Class.forName(classFullPath);
try {
Object instance = aClass.newInstance();
ioc.put(className, instance);//这里可以将类名的首字母小写
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
//如果这个类没有@Commponent, 说明不是一个 spring
bean
System.out.println("不是一个 bean = " + clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("=======================================");
}
}
}
}
/**
韩顺平 Java 工程师
* 返回 ioc 注入的指定 bean
* @param name
* @return
*/
public Object getBean(String name) {
return ioc.get(name);
}
}
4 测试
public class HspSpringAnnoationMain {
public static void main(String[] args) {
//创建我们的 spring 容器对象
HspSpringApplicationContext hspSpringApplicationContext =
new HspSpringApplicationContext(HspSpringConfig.class);
//可以看看注入了哪些 bean. ConcurrentHashMap<String, Object> ioc =
hspSpringApplicationContext.getIoc();
//遍历一把
Enumeration<String> keys = ioc.keys();
for (String key : ioc.keySet()) {
System.out.println("bean id= " + key + " bean 对象= " + ioc.get(key));
}
//指定获取 bean
UserDao userDao =
(UserDao) hspSpringApplicationContext.getBean("UserDao");
//调用方法. userDao.hi();
}
}
自动装配
基本说明
基于注解配置 bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource
@AutoWired 的规则说明
- 在 IOC 容器中查找待装配的组件的类型,如果有唯一的 bean 匹配,则使用该 bean 装配
- 如待装配的类型对应的 bean 在 IOC 容器中有多个,则使用待装配的属性的属性名作 为 id 值再进行查找, 找到就装配,找不到就抛异常
@Resource 的规则说明
- @Resource 有两个属性是比较重要的,分是 name 和 type,Spring 将@Resource 注解的name 属性解析为 bean 的名字,而 type 属性则解析为 bean 的类型.所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略(要求容器中只能有一个这样类型的对象)
- 如果@Resource 没有指定 name 和 type ,则先使用byName注入策略, 如果匹配不上, 再使用 byType 策略, 如果都不成功,就会报错
4.@Autowired @Qualifier(value = "userService02")
等于
@Resource(name="userService02")
注解
- @Component:用于定义一个非业务逻辑的bean,例如一个数据库访问类或一个消息处理类。
- @Service:用于定义一个业务逻辑的bean,例如一个用户服务或一个商品服务
- @Repository:用于定义一个数据访问的bean,例如一个用户实体或一个商品实体。
- @Controller:用于定义一个控制器bean,例如一个处理HTTP请求的控制器
泛型依赖注入
基本说明
- 为了更好的管理有继承和相互依赖的 bean 的自动装配,spring 还提供基于泛型依赖的 注入机制
- 在继承关系复杂情况下,泛型依赖注入就会有很大的优越性
代码
1 创建depinjection
package com.yb.depinjection;
public class Phone {
}
package com.yb.depinjection;
public class Book {
}
package com.yb.depinjection;
public abstract class BaseDao<T> {
public abstract void save();
}
package com.yb.depinjection;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao extends BaseDao<Book> {
@Override
public void save() {
System.out.println("BookDao 的 save()");
}
}
package com.yb.depinjection;
import org.springframework.stereotype.Repository;
@Repository
public class PhoneDao extends BaseDao<Phone> {
@Override
public void save() {
System.out.println("PhoneDao 的 save()");
}
}
package com.yb.depinjection;
import org.springframework.beans.factory.annotation.Autowired;
public class BaseService<T> {
@Autowired
private BaseDao<T> baseDao;
public void save() {
baseDao.save();
}
}
package com.yb.depinjection;
import org.springframework.stereotype.Service;
@Service
public class BookService extends BaseService<Book> {
}
package com.yb.depinjection;
import org.springframework.stereotype.Service;
@Service
public class PhoneService extends BaseService<Phone> {
}
<context:component-scan base-package="com.yb.depinjection"/>
测试类
@Test
public void setProByDepinjectionAutowired() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans5.xml");
BookService bookService = ioc.getBean(BookService.class);
bookService.save();
PhoneService phoneService = ioc.getBean(PhoneService.class);
phoneService.save();
}
AOP
需求说明
- 有 Vehicle(交通工具接口, 有一个 run 方法), 下面有两个实现类 Car 和 Ship
- 当运行 Car 对象 的 run 方法和 Ship 对象的 run 方法时,输入如下内容, 注意观察前后 有统一的输出.

解决方案-动态代理方式
代码实现
动态代理解决思路,在调用方法时,使用反射机制,根据方法去决定调用哪个对象方法
package com.yb.proxy2;
public interface Vehicle {
public void run();
}
package com.yb.proxy2;
public class Car implements Vehicle {
@Override
public void run() {
//System.out.println("交通工具开始运行了...");
System.out.println("小汽车在公路 running..");
//System.out.println("交通工具停止运行了...");
}
}
package com.yb.proxy2;
public class Ship implements Vehicle {
@Override
public void run() {
//System.out.println("交通工具开始运行了...");
System.out.println("大轮船在水上 running...");
//System.out.println("交通工具停止运行了...");
}
}
package com.yb.proxy2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class VehicleProxyProvider {
//设置一个将来要运行的对象,只要是实现了 Vehicle 接口的就 ok
private Vehicle target_vehicle;
public VehicleProxyProvider(Vehicle target_vehicle) {
this.target_vehicle = target_vehicle;
}
//编写代码,返回一个 Vehicle 的代理对象
public Vehicle getProxy() {
// 1.获取类加载对象
ClassLoader loader = target_vehicle.getClass().getClassLoader();
// 2.获取接口类型数组, 为什么是接口信息,而不是方法,是因为我们都是走接口的
Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();
// 3. 创 建 InvocationHandler 对 象 - 以 匿 名 内 部 类 的 方 式 方 式 来 获 取InvocationHandler
// 这个对象有一个方法是 invoke 到时可以通过反射,动态调用目标对象的方法
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("交通工具开始运行了...");
//这个地方的方法,就是你调用时,动态传入的可能是 run , 可能是 hi 等
Object result = method.invoke(target_vehicle, args);
System.out.println("交通工具停止运行了...");
return result;
}
};
//将上面的 loader, interfaces, invocationHandler 构建一个 Vehicle
//的代理对象.
Vehicle proxy = (Vehicle) Proxy.newProxyInstance(loader, interfaces, invocationHandler);
return proxy;
}
}
package com.yb.proxy2;
public class Test {
public static void main(String[] args) {
//这里可以切换 Vehicle 的 实现类(对象)
Vehicle vehicle = new Car();
VehicleProxyProvider vehicleProxyProvider =
new VehicleProxyProvider(vehicle);
//给学员看一下 proxy 的结构.
Vehicle proxy = vehicleProxyProvider.getProxy();
System.out.println("proxy 编译类型是 Vehicle");
System.out.println("proxy 运行类型" + proxy.getClass());
//动态代理的 动态怎么体现
//老韩认为
//1. proxy 运行类型是 com.sun.proxy.$Proxy0 该类型被转型成 Vehicle
// 因此可以调用 Vehicle 的接口方法
//2. 当执行 run() 的时候会调用, 根据 Java 的动态绑定机制, 这时直接调用 Car的 run (), 而是 proxy 对象的 invocationHandler 的 invoke 方法 (!!!!!!)
//3. invoke 方法使用反射机制来调用 run()方法注意这个 run 方法也可以是Vehicle 的其它方法)
// 这时就可以在调用 run()方法前,进行前置处理和后置处理
//4. 也就是说 proxy 的 target_vehicle 运行类型只要是实现了 Vehicle 接口
// ,就可以去调用不同的方法, 是动态的,变化的,底层就是 使用反射完成的.
proxy.run();
}
}
动态代理深入
● 需求说明
- 有一个 SmartAnimal 接口,可以完成简单的加减法, 要求在执行 getSum()和 getSub() 时,输出执行前,执行过程,执行后的日志输出,请思考如何实现.
方法执行开始-日志-方法名-getSub-参数 [10.0, 2.0]
方法内部打印 result = 8.0
方法执行正常结束-日志-方法名-getSub-结果 result= 8.0
方法最终结束-日志-方法名-getSub
= == == == == == == == == == == = = = = =
方法执行开始-日志-方法名-getSum-参数 [10.0, 2.0]
方法内部打印 result = 12.0
方法执行正常结束-日志-方法名-getSum-结果 result= 12.0
方法最终结束-日志-方法名-getSum
使用动态代理
public class MyProxyProvider {
private SmartAnimalable target_obj;
// 构造器
public MyProxyProvider(SmartAnimalable target_obj) {
this.target_obj = target_obj;
}
public SmartAnimalable getProxy() {
// 1.获取类加载对象
ClassLoader loader = target_obj.getClass().getClassLoader();
// 2.获取接口类型数组
Class<?>[] interfaces = target_obj.getClass().getInterfaces();
// 3.获取 InvocationHandler 以匿名内部类的方式方式来获取 InvocationHandler
InvocationHandler h = new InvocationHandler() {
// 4.以动态代理的方式调用目标对象的目标方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
String methodName = method.getName();
try {
// 1. 在调用目标方法之前打印“方法开始”日志
System.out.println("日志--方法名:" + methodName + "--方法开始-- 参数:" + Arrays.asList(args));
// 2. 调用目标方法并接收返回值
result = method.invoke(target_obj, args);
// 3. 在目标方法结束后打印“方法结束”日志
System.out.println("日志--方法名:" + methodName
+ "--方法正常结束--结果:result=" + result);
} catch (Exception e) {
// 4.如果目标方法抛出异常,打印“方法异常”日志
e.printStackTrace();
System.out.println("日志--方法名:" + methodName
+ "--方法抛出异常--异常类型:" + e.getClass().getName());
} finally {
// 5.在 finally 中打印“方法最终结束”日志
System.out.println("日志--方法名:" + methodName + "--方法最终结束");
}
// 6. 返回目标方法的返回值
return result;
}
};
//生成 SmartAnimaleable 的代理对象
//需要三个参数 ,
//1.就是 loader(获取类加载对象)
//2.接口数组
//3.InvocationHandler 对象 [这个相对麻烦..]
SmartAnimalable proxy = (SmartAnimalable) Proxy.newProxyInstance(
loader, interfaces, h);
return proxy;
}
}
public class SmartDog implements SmartAnimalable {
@Override
public float getSum(float i, float j) {
// System.out.println("日志--方法名--getSum 方法开始--参数:" + i + "," + j);
float result = i + j;
// System.out.println("方法内部打印:result=" + result);
// System.out.println("日志--方法名--getSum 方法结束--结果:result=" + result);
return result;
}
@Override
public float getSub(float i, float j) {
// System.out.println("日志--方法名--getSub 方法开始--参数:" + i + "," + j);
float result = i - j;
// System.out.println("方法内部打印:result=" + result);
// System.out.println("日志--方法名--getSub 方法结束--结果:result=" + result);
return result;
}
}
测试
@Test
public void smartDogTestByProxy() {
SmartAnimalable smartDog = new SmartDog();
//老韩解读
//1.使用动态代理的方式来调用
//2.当使用动态代理调用是 SmartDog 的各个函数的日志输出就可以不写了,有代理对
象帮我们完成. MyProxyProvider provider = new MyProxyProvider(smartDog);
smartDog = provider.getProxy();
smartDog.getSum(10.78f, 89.7f);
smartDog.getSub(10.78f, 89.7f);
}
问题引出
在 MyProxyProvider.java 中, 我们的输出语句功能比较弱,在实际开发中,我们希望是以一个方法的形式,嵌入到真正执行的目标方法前,怎么办?
public class MyProxyProvider {
private SmartAnimalable target_obj;
// 构造器
public MyProxyProvider(SmartAnimalable target_obj) {
this.target_obj = target_obj;
}
public void before(Object proxy, Method method, Object[] args) {
// 1. 在调用目标方法之前打印“方法开始”日志
System.out.println("before 日志--方法名:" + method.getName() + "--方法开 始--参数:" + Arrays.asList(args));
}
public void after(Method method, Object result) {
// 3. 在目标方法结束后打印“方法结束”日志
System.out.println("after 日志--方法名:" + method.getName()
+ "--方法正常结束--结果:result=" + result);
}
//可以更多方法
public SmartAnimalable getProxy() {
// 1.获取类加载对象
ClassLoader loader = target_obj.getClass().getClassLoader();
// 2.获取接口类型数组, 为什么是接口信息,而不是方法,是因为我们都是走接口的.
Class<?>[] interfaces = target_obj.getClass().getInterfaces();
// 3. 创 建 InvocationHandler 对 象 - 以 匿 名 内 部 类 的 方 式 方 式 来 获 取InvocationHandler
//
InvocationHandler h = new InvocationHandler() {
// 4.以动态代理的方式调用目标对象的目标方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
String methodName = method.getName();
try {
before(proxy, method, args);//切入到目标方法前
// 2. 调用目标方法并接收返回值
result = method.invoke(target_obj, args);
after(method, result);//切入到目标方法后
} catch (Exception e) {
// 4.如果目标方法抛出异常,打印“方法异常”日志
e.printStackTrace();
System.out.println("日志--方法名:" + methodName
+ "-- 方 法 抛 出 异 常 -- 异 常 类 型 : " +
e.getClass().getName());
} finally {
// 5.在 finally 中打印“方法最终结束”日志
System.out.println("日志--方法名:" + methodName + "--方法最终 结束");
}
// 6. 返回目标方法的返回值
return result;
}
};
//生成 SmartAnimaleable 的代理对象
//需要三个参数 ,
//1.就是 loader(获取类加载对象)
//2.接口数组
//3.InvocationHandler 对象 [这个相对麻烦..]
SmartAnimalable proxy = (SmartAnimalable) Proxy.newProxyInstance(
loader, interfaces, h);
return proxy;
}
}
该方法问题分析:耦合度高
对于以上方法进行解耦 (自己写一个切入类)
/**
* @author 韩顺平
* @version 1.0
* 自己写的一个切入类
*/
public class HspAOP {
public static void before(Object proxy, Method method, Object[] args) {
// 1. 在调用目标方法之前打印“方法开始”日志
System.out.println("自己的切入类的 before 日志--方法名:" + method.getName()
+ "--方法开始--参数:" + Arrays.asList(args));
}
public static void after(Method method, Object result) {
// 3. 在目标方法结束后打印“方法结束”日志
System.out.println("自己的切入类的 after 日志--方法名:" + method.getName()
+ "--方法正常结束--结果:result=" + result);
}
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
String methodName = method.getName();
try {
//before(proxy,method,args);//切入到目标方法前
HspAOP.before(proxy, method, args);
// 2. 调用目标方法并接收返回值
result = method.invoke(target_obj, args);
//after(method, result);//切入到目标方法后
HspAOP.after(method, result);
} catch (Exception e) {
// 4.如果目标方法抛出异常,打印“方法异常”日志
e.printStackTrace();
System.out.println("日志--方法名:" + methodName
+ "--方法抛出异常--异常类型:" + e.getClass().getName());
} finally {
// 5.在 finally 中打印“方法最终结束”日志
System.out.println("日志--方法名:" + methodName + "--方法最终结束");
}
// 6. 返回目标方法的返回值
return result;
}
再次分析-提出 Spring AOP
1 土方法 不够灵活
2 土方法 复用性差
3 土方法 还是一种硬编码(因为没有注解和反射支撑)
4 Spring AOP 闪亮登场-底层是 ASPECTJ
AOP 的基本介绍
AOP 的全称(aspect oriented programming) ,面向切面编程


● AOP 实现方式
- 基于动态代理的方式[内置 aop 实现]
- 使用框架 aspectj 来实现
AOP 编程快速入门
在切面类中声明通知方法
前置通知:@Before
返回通知:@AfterReturning
异常通知:@AfterThrowing
后置通知:@After
环绕通知:@Around

package com.yb.aspectj;
public interface SmartAnimalable {
float getSum(float i, float j);
float getSub(float i, float j);
}
package com.yb.aspectj;
import org.springframework.stereotype.Component;
@Component // 使用@Component 当spring容器启动时,将SmartDog注入到容器中
public class SmartDog implements SmartAnimalable {
@Override
public float getSum(float i, float j) {
float result = i + j;
System.out.println("getSum() 方法内部打印 result= " + result);
return result;
}
@Override
public float getSub(float i, float j) {
float result = i - j;
System.out.println("getSub() 方法内部打印 result= " + result);
return result;
}
}
韩 老 师 提 醒 : SmartAnimalAspect 作 用 就 是 去 接 管 切 面 编 程 , 此 时 原 来 的MyProxyProvider 类就可以拿掉了.
package com.yb.aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.aspectj.lang.Signature;
import java.util.Arrays;
/**
* 使用切面编程来替代原来的动态代理类,机制是一样的. * @author Administrator
*/
@Aspect //表示这个类是一个切面类
@Component //需要加入到 IOC 容器 注入 SmartAnimalAspect 类到容器中
public class SmartAnimalAspect {
//这个就对应动态代理类的
//System.out.println(" 日 志 -- 方 法 名 : "+methodName+"-- 方 法 开 始 -- 参 数 :"+Arrays.asList(args));
@Before(value = "execution(public float com.yb.aspectj.SmartDog.getSum(float, float))")
public void showBeginLog(JoinPoint joinPoint) {
System.out.println("前置通知");
Signature signature = joinPoint.getSignature();
// 1. 在调用目标方法之前打印“方法开始”日志
System.out.println("日志--方法名:" + signature.getName() + "--方法开始--参数: " + Arrays.asList(joinPoint.getArgs()));
}
//这个就对应动态代理类的
//System.out.println("日志--方法名:"+methodName+"-- 方法正常结束--结果:result = "+result);
@AfterReturning(value = "execution(public float com.yb.aspectj.SmartDog.getSum(float, float))")
public void showSuccessEndLog(JoinPoint joinPoint) {
System.out.println("返回通知");
Signature signature = joinPoint.getSignature();
// 3. 在目标方法结束后打印“方法结束”日志
System.out.println("日志--方法名:" + signature.getName() + "--方法正常结束 -- ~");
}
//这个就对应动态代理类的
//System.out.println("日志--方法名:"+methodName+"--方法抛出异常--异常类型:"+e.getClass().getName());
@AfterThrowing(value = "execution(public float com.yb.aspectj.SmartDog.getSum(float, float))")
public void showExceptionLog() {
System.out.println("异常通知");
}
//这个就对应动态代理类的
//System.out.println("日志--方法名:"+methodName+"--方法最终结束");
@After(value = "execution(public float com.yb.aspectj.SmartDog.getSum(float, float))")
public void showFinallyEndLog() {
System.out.println("最终通知");
}
}
<!-- 配置自动扫描的包,根据实际情况配置即可 -->
<context:component-scan base-package="com.yb.aspectj"/>
<!-- 开启基于注解的 AOP 功能 -->
<aop:aspectj-autoproxy/>
package com.yb.aspectj;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopAspectjTest {
/**
* spring aop 方式切入前置 before 和 后置 after 方法
*/
@Test
public void smartDogTestByProxy() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans6.xml");
//通过接口来
SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
float sum = bean.getSum(101.0f, 11.2f);
System.out.println("sum= " + sum);
//getSub 没有 AOP 注解,所以就是普通调用
//bean.getSub(30.6f, 43.2f);
System.out.println("-----------------");
}
}
细节说明
关于切面类方法命名可以自己规范一下, 比如 showBeginLog() . showSuccessEndLog() showExceptionLog(), showFinallyEndLog()
切入表达式的更多配置,比如使用模糊配置
@Before("execution(* com.yb.aspectj.homework.*.*(..))")
表示所有访问权限,所有包的下所有有类的所方法,都会被执行该前置通知方法
@Before("execution(* com.yb.aspectj.homework.*.*(..))")
当 spring 容器开启了
<aop:aspectj-autoproxy/>
, 我们获 取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型了!当 spring 容器开启了
<aop:aspectj-autoproxy/>
, 我们获取注入的对象, 也可以通过 id 来获取, 但是也要转成接口类型.
作业
- 有接口 UsbInterface (方法 work)
- 实现子类 Phone 和 Camera 实现 UsbInterface
- 请在 SmartAnimalAspect 切面类, 写一个方法(可输出日志信息) 等作为前置通知, 在Phone 和 Camera 对象执行 work 方法前调用
代码
package com.yb.aspectj.homework;
import org.springframework.stereotype.Component;
@Component
public class Camera implements UsbInterface{
@Override
public void work() {
System.out.println("Camera的work方法");
}
}
package com.yb.aspectj.homework;
import org.springframework.stereotype.Component;
@Component
public class Phone implements UsbInterface{
@Override
public void work() {
System.out.println("Phone的work方法");
}
}
package com.yb.aspectj.homework;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SmartAnimalAspect {
// @Before(value = "execution(public float com.yb.aspectj.SmartDog.getSum(float, float))")
// @Before("execution(* com.LightseaBlue.Spring..*(..))")
@Before("execution(public void com.yb.aspectj.homework.*.work())")
public void showBeginLog() {
System.out.println("前置操作");
}
}
package com.yb.aspectj.homework;
public interface UsbInterface {
public void work();
}
package com.yb.aspectj.homework;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test {
@Test
public void test1(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans6.xml");
UsbInterface phone = (UsbInterface)ioc.getBean("phone");
UsbInterface camera = (UsbInterface)ioc.getBean("camera");
phone.work();
camera.work();
}
}
<!-- 配置自动扫描的包,根据实际情况配置即可 -->
<context:component-scan base-package="com.yb.aspectj.homework"/>
<!-- 开启基于注解的 AOP 功能 -->
<aop:aspectj-autoproxy/>
AOP-切入表达式
具体使用



注意事项和细节
- 切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效
- 切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效
- 切入表达式也可以对没有实现接口的类,进行切入
- 老师补充: 动态代理 jdk 的 Proxy 与 Spring 的 CGlib
- JDK动态代理是面向接口的,只能增强实现类中接口中存在的方法。CGlib是面向父类的,可以增强父类的所有方法
- JDK得到的对象是JDK代理对象实例,而CGlib得到的对象是被代理对象的子类
AOP-JoinPoint
通过 JoinPoint 可以获取到调用方法的签名
public void beforeMethod(JoinPoint joinPoint) {
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(value = "execution(public float com.hspedu.spring.aop.joinpoint.SmartDog.getSum(float, float))", returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
System.out.println("返回通知" + "--结果是--" + res );
}
AOP-异常通知中获取异常
异常通知方法中获取异常
@AfterThrowing(value = "execution(public float com.hspedu.spring.aop.joinpoint.SmartDog.getSum(float, float))", throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
System.out.println("异常通知 -- 异常信息--" + throwable);
}
AOP-切入点表达式重用
● 切入点表达式重用
@Aspect //表示这个类是一个切面类
@Component //需要加入到 IOC 容器
public class SmartAnimalAspect {
//=====AOP-切入点表达式重用 start ======
/*
* 这样定义的一个切入点表达式,就可以在其它地方直接使用
*/
@Pointcut(value = "execution(public float com.hspedu.spring.aop.joinpoint.SmartDog.getSum(float, float))")
public void myPointCut() {
}
// @Before(value="execution(public floatcom.hspedu.spring.aop.joinpoint.SmartDog.getSum(float,float))")
@Before(value = "myPointCut()",returning="result")
public void showBeginLog(JoinPoint joinPoint,object result) { //前置方法
//得到方法的签名
// 调 用 前 置 通 知 对 应 的 方 法 签 名 : floatcom.hspedu.spring.aop.joinpoint.SmartDog.getSum( float,float)
Signature signature = joinPoint.getSignature();
//得到方法名. String method_name = signature.getName();
//得到参数
Object[] args = joinPoint.getArgs();
System.out.println("前置通知" + "--调用的方法是 " + method_name + "--参数是 --" + Arrays.asList(args));
}
//@After(value = "execution(public floatcom.hspedu.spring.aop.joinpoint.SmartDog.getSum(float,float))")
@After(value = "myPointCut()")
public void showFinallyEndLog() {
System.out.println("最终通知 -- AOP-切入点表达式重用");
}
}
AOP-切面优先级问
如果同一个方法,有多个切面在同一个切入点切入,那么执行的优先级如何控制
@order(value=n) 来控制 ,n 值越小,优先级越高.
注意事项和细节说明
不能理解成:优先级高的每个消息通知都先执行,这个和方法调用机制(和 Filter 过滤器 链式调用类似)

AOP-基于 XML 配置 AOP
前面我们是通过注解来配置 aop 的,在 spring 中,我们也可以通过 xml 的方式来配置 AOP
package com.yb.web;
public class SmartAnimalAspect {
public void showBeginLog(JoinPoint joinPoint) { //前置通知
//得到方法的签名
Signature signature = joinPoint.getSignature();
//得到方法名.
String method_name = signature.getName();
//得到参数
Object[] args = joinPoint.getArgs();
System.out.println("XML 方式: 前置通知" + "--调用的方法是 " + method_name + "--参数是 --" + Arrays.asList(args));
}
public void showFinallyEndLog() {
System.out.println("XML 方式: 最终通知 -- AOP-切入点表达式重用");
}
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
System.out.println("XML 方式: 返回通知" + "--结果是--" + res);
}
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
System.out.println("XML 方式: 异常通知 -- 异常信息--" + throwable);
}
}
<?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"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.hspedu.spring.aop.xml"/>
<!-- 开启基于注解的 AOP 功能 -->
<aop:aspectj-autoproxy/>
<!-- 配置 SmartAnimalAspect bean -->
<bean id="smartAnimalAspect" class="com.hspedu.spring.aop.xml.SmartAnimalAspect"/>
<!--配置 SmartDog-->
<bean class="com.hspedu.spring.aop.xml.SmartDog" id="smartDog"/>
<aop:config>
<!-- 配置统一切入点 -->
<aop:pointcut expression="execution(public float com.hspedu.spring.aop.xml.SmartDog.getSum(float, float))"
id="myPointCut"/>
<aop:aspect ref="smartAnimalAspect" order="1">
<!-- 配置各个通知对应的切入点 -->
<aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
<aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" returning="res"/>
<aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="throwable"/>
<aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/>
<!-- 还可以配置环绕通知 -->
<!-- <aop:around method=""/> -->
</aop:aspect>
</aop:config>
</beans>
@Test
public void testAopJoinPoint() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("aop_ioc.xml");
SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
bean.getSum(10.0f, 11.2f);
System.out.println("-----------------");
}
作业
- 请编写一个 Cal 接口 方法 cal1(int n) 计算 1+2..+n 方法 cal2(int n) 计算 1 * 2 * ... * n
- 实现类 MyCal implements Cal
- 请分别使用注解方式 / XML 配置方式 完成 AOP 编程 (1) 在执行 cal1 前打印开始执行的时间,在 执行完后打印时间 (2) 在执行 cal2 前打印开始执行的时间,在 执行完后打印时间
cal1方法 开始执行时间为1714837626608
1到1000000的和为:1784293664
cal1方法 结束执行时间为1714837626609
注解方式
package com.yb.aspectj.homework2;
public interface Cal {
void cal1(int n);
void cal2(int n);
}
package com.yb.aspectj.homework2;
import org.springframework.stereotype.Component;
@Component
public class MyCal implements Cal{
@Override
public void cal1(int n) {
int res = 0;
for(int i = 1; i <= n ; i++){
res = res + i;
}
System.out.println("1到" + n + "的和为:" + res);
}
@Override
public void cal2(int n) {
double res1 = 1;
for(int i = 1; i <= n ; i++){
res1 = res1 * i;
}
System.out.println("1到" + n + "的积为:" + res1);
}
}
package com.yb.aspectj.homework2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CalAspect {
@Before(value = "execution(public void com.yb.aspectj.homework2.MyCal.*(..))")
public void showBeginLog(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
long stime = System.currentTimeMillis();
System.out.println(signature.getName()+"方法 开始执行时间为" +stime);
}
@AfterReturning(value = "execution(public void com.yb.aspectj.homework2.MyCal.*(..))")
public void showSuccessEndLog(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
long etime = System.currentTimeMillis();
// 计算执行时间
System.out.printf(signature.getName()+"方法 结束执行时间为"+etime);
}
}
@Test
public void test2(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans7.xml");
Cal bean = ioc.getBean(Cal.class);
bean.cal1(1000000);
System.out.println();
System.out.println("===================");
bean.cal2(100);
}
<context:component-scan base-package="com.yb.aspectj.homework2"/>
<!-- 开启基于注解的 AOP 功能 -->
<aop:aspectj-autoproxy/>
XML 配置方式
package com.yb.aspectj.homework3;
public interface Cal {
void cal1(int n);
void cal2(int n);
}
package com.yb.aspectj.homework3;
public class CalAspect {
public void showBeginLog(){
long stime = System.currentTimeMillis();
System.out.println(stime);
}
public void showSuccessEndLog(){
long etime = System.currentTimeMillis();
// 计算执行时间
System.out.printf(String.valueOf(etime));
}
}
package com.yb.aspectj.homework3;
public class MyCal implements Cal {
@Override
public void cal1(int n) {
int res = 0;
for(int i = 1; i <= n ; i++){
res = res + i;
}
System.out.println("1到" + n + "的和为:" + res);
}
@Override
public void cal2(int n) {
double res1 = 1;
for(int i = 1; i <= n ; i++){
res1 = res1 * i;
}
System.out.println("1到" + n + "的积为:" + res1);
}
}
@Test
public void test(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans8.xml");
Cal bean = ioc.getBean(Cal.class);
bean.cal1(1000000);
}
<?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"
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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置切面类的bean-->
<bean id="calAspect" class="com.yb.aspectj.homework3.CalAspect" />
<!-- 配置代理对象的bean对象-->
<bean id="myCal" class="com.yb.aspectj.homework3.MyCal" />
<aop:config>
<!-- 配置切点表达式-->
<aop:pointcut id="myPointCut" expression="execution(public void com.yb.aspectj.homework3.MyCal.*(..))"/>
<aop:aspect ref="calAspect" >
<!-- 配置各个切入点-->
<aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
<aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
手动实现 Spring 底层机制 【老韩新增】 【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】
原生 Spring 如何实现依赖注入和 singleton、prototype
spring需要用到的pom依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.8</version>
</dependency>
package com.yb.spring.component;
import org.springframework.stereotype.Component;
//也可以用 @Controller
@Component
//@Scope(value = "prototype")
public class UserAction {
}
package com.yb.spring.component;
import org.springframework.stereotype.Component;
// 也可以用 @Repository
@Component
public class UserDao {
public void hi() {
System.out.println("UserDao hi() 被调用...");
}
}
package com.yb.spring.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
// 也可以用 @Service
@Component
public class UserService {
//依赖注入
@Autowired
private UserDao userDao;
public void m1() {
userDao.hi();
}
}
package com.yb.spring;
import com.yb.spring.component.UserDao;
import com.yb.spring.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppMain {
public static void main(String[] args) {
//获取 bean 容器/ioc 容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
System.out.println(ioc.getBean("userAction"));
System.out.println(ioc.getBean("userAction"));
UserDao userDao = (UserDao) ioc.getBean("userDao");
System.out.println(userDao);
UserService userService = (UserService) ioc.getBean("userService");
System.out.println(userService);
//====测试依赖注入=======
userService.m1();
//关闭容器
((ConfigurableApplicationContext) ioc).close();
}
}
思考问题
1 Spring 底层实现, 如何实现 IOC 容器创建和初始化【前面我们实现过,现在要再深入】
2 Spring 底层实现, 如何实现 getBean, 根据 singleton 和 prototype 来返回 bean 实例
继续思考-原生 Spring 如何实现 BeanPostProcessor
对上面代码进行修改
<?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 名称空间 -->
<context:component-scan base-package="com.yb.spring.component"/>
<!-- bean 后置处理器的配置 -->
<bean id="myBeanPostProcessor" class="com.yb.spring.process.MyBeanPostProcessor"/>
</beans>
增加后置处理器
package com.yb.spring.process;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 在 bean 初始化之前完成某些任务
*
* @param bean : 就是 ioc 容器返回的 bean 对象, 如果这里被替换会修改,则返
* 回的 bean 对象也会被修改
* @param beanName: 就是 ioc 容器配置的 bean 的名称
* @return Object: 就是返回的 bean 对象
*/
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization 被 调 用 " + beanName + " bean= " + bean.getClass());
return bean;
}
/**
* 在 bean 初始化之后完成某些任务
*
* @param bean : 就是 ioc 容器返回的 bean 对象, 如果这里被替换会修改,则返
* 回的 bean 对象也会被修改
* @param beanName: 就是 ioc 容器配置的 bean 的名称
* @return Object: 就是返回的 bean 对象
*/
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("postProcessAfterInitialization 被调用 " + beanName + " bean= " + bean.getClass());
return bean;
}
}
在UserService中添加init方法
package com.yb.spring.component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
// 也可以用 @Service
@Component
public class UserService {
//依赖注入
@Autowired
private UserDao userDao;
public void m1() {
userDao.hi();
}
@PostConstruct
public void init() {
System.out.println("初始化业务...");
}
}
简单分析 AOP 和 BeanPostProcessor 关系
AOP 实现 Spring 可以通过给一个类,加入注解 @EnableAspectJAutoProxy 来指定
老韩解读
AOP 底层是基于 BeanPostProcessor 机制的.
即在 Bean 创建好后,根据是否需要 AOP 处理,决定返回代理对象,还是原生 Bean
在返回代理对象时,就可以根据要代理的类和方法来返回
其实这个机制并不难,本质就是在 BeanPostProcessor 机制 + 动态代理技术
Spring 底层实现, 如何实现 AOP 编程
我们的目标: 不用 Spring 框架, 模拟 Spring 底层实现, 也能完成相同的功能
Spring 整体架构分析
一图胜千言

手动实现 Spring 底层机制【初始化 IOC 容器+依赖注入+BeanPostProcessor 机制+AOP】
实现任务阶段 1
知识扩展:类加载器
● java 的类加载器 3 种
Bootstrap 类加载器--------------对应路径 jre/lib
Ext 类加载器--------------------对应路径 jre/lib/ext
App 类加载器-------------------对应路径 classpath
● classpath 类路径,就是 java.exe 执行时,指定的路径,
编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象

步骤1 : 创建包com.yb.spring.annotation 以及Component 和 ComponentScan 注解文件
package com.yb.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
package com.yb.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value();
}
步骤2 : 创建包 com.yb.spring.component 以及 MonsterDao 和 MonsterService java文件
package com.yb.spring.component;
import com.yb.spring.annotation.Component;
@Component("monsterDao")
public class MonsterDao {
}
package com.yb.spring.component;
import com.yb.spring.annotation.Component;
@Component(value = "monsterService")
public class MonsterService {
}
步骤3 : 创建com.yb.spring.ioc.HspSpringApplicationContext
package com.yb.spring.ioc;
import com.yb.spring.annotation.Component;
import com.yb.spring.annotation.ComponentScan;
import java.io.File;
import java.net.URL;
public class HspSpringApplicationContext {
private Class configClass;
public HspSpringApplicationContext(Class configClass) {
this.configClass = configClass;
//1. 解析配置类
//2. 获取到配置类的 @ComponentScan("com.hspedu.spring.component")
ComponentScan componentScan = (ComponentScan)
this.configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
System.out.println("扫描路径 = " + path);
//3. 获取扫描路径下所有的类文件
//(1) 先得到类加载器, 使用 App 方式来加载.
ClassLoader classLoader = HspSpringApplicationContext.class.getClassLoader();
//在获取某个包的 d 对应的 URL 时,要求是 com/hspedu/spring/component
//URL resource = classLoader.getResource("com/hspedu/spring/component");
//(2) 将 path 转成 形式为 com/hspedu/spring/component
path = path.replace(".", "/");
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
System.out.println("=======================================");
System.out.println("文件绝对路径 = " + fileAbsolutePath);
if (fileAbsolutePath.endsWith(".class")) {//说明是类文件
//通过类加载器获取来类文件的 Clazz 对象
// 先 得 到 类 的 完 整 类 路 径 形 式 为com.hspedu.spring.component.MonsterService
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\")
+ 1, fileAbsolutePath.indexOf(".class"));
String classFullPath = path.replace("/", ".") + "." + className;
System.out.println("类名 = " + className);
System.out.println("类的全路径 = " + classFullPath);
try {
//获取到扫描包下的类的 clazz 对象
Class<?> clazz = classLoader.loadClass(classFullPath);
if (clazz.isAnnotationPresent(Component.class)) {
//如果这个类有@Commponent, 说明是一个 spring bean
System.out.println("是一个 bean = " + clazz);
} else {
//如果这个类没有@Commponent, 说明不是一个 spring bean
System.out.println("不是一个 bean = " + clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("=======================================");
}
}
}
}
public Object getBean(String name) {
return null;
}
}
步骤4 : 创建com.yb.spring.ioc.HspSpringConfig
package com.yb.spring.ioc;
import com.yb.spring.annotation.ComponentScan;
@ComponentScan(value = "com.yb.spring.component")
public class HspSpringConfig {
}
步骤5 :创建测试文件com.yb.spring.AppMain
package com.yb.spring;
import com.yb.spring.ioc.HspSpringApplicationContext;
import com.yb.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) {
//创建我们的 spring 容器对象
HspSpringApplicationContext hspSpringApplicationContext = new HspSpringApplicationContext(HspSpringConfig.class);
}
}
实现任务阶段 2
说明:扫描将 bean 信息封装到 BeanDefinition 对象, 并放入到 Map

步骤1 : 创建 com.yb.spring.annotation.Scope
package com.yb.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value(); //不需要给默认值
}
步骤2 : 修 改MonsterService , 增 加 @Scope
package com.yb.spring.component;
import com.yb.spring.annotation.Component;
import com.yb.spring.annotation.Scope;
@Component(value = "monsterService")
@Scope("prototype")
public class MonsterService {
}
步骤3 : 创建 com.yb.spring.ioc.BeanDefinition
package com.yb.spring.ioc;
/**
* 1. 在扫描时,将 bean 信息,封装到 beandefinition 对象中
* 2. 然后将 beandefinition 对象 , 放入到 spring 容器的集合中
*/
public class BeanDefinition {
private Class clazz; //bean 对应的 Class 对象
private String scope;//bean 的作用域 singleton/prototype
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
@Override
public String toString() {
return "BeanDefinition{" +
"clazz=" + clazz +
", scope='" + scope + '\'' +
'}';
}
}
步骤4 : com.yb.spring.ioc.HspSpringApplicationContext 将扫描到的 Bean 信息封装到 BeanDefinition 对象中,并保存到 Map
package com.yb.spring.ioc;
import com.yb.spring.annotation.Component;
import com.yb.spring.annotation.ComponentScan;
import com.yb.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
public class HspSpringApplicationContext {
private Class configClass;
//如果 bean 是单例的,就直接放在这个 单例 bean 对象池
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
//将 bean 的定义,放在这个 beanDefinitionMap 集合
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public HspSpringApplicationContext(Class configClass) {
//通过扫描,得到 beanDefinition 的 map
beanDefinitionsByscan(configClass);
System.out.println(beanDefinitionMap);
}
private void beanDefinitionsByscan(Class configClass) {
this.configClass = configClass;
//1. 解析配置类
//2. 获取到配置类的 @ComponentScan("com.hspedu.spring.component")
ComponentScan componentScan = (ComponentScan)
this.configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
System.out.println("扫描路径 = " + path);
//3. 获取扫描路径下所有的类文件
//(1) 先得到类加载器, 使用 App 方式来加载.
ClassLoader classLoader = HspSpringApplicationContext.class.getClassLoader();
//在获取某个包的 d 对应的 URL 时,要求是 com/hspedu/spring/component
//URL resource = classLoader.getResource("com/hspedu/spring/component");
//(2) 将 path 转成 形式为 com/hspedu/spring/component``
path = path.replace(".", "/");
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
System.out.println("=======================================");
System.out.println("文件绝对路径 = " + fileAbsolutePath);
if (fileAbsolutePath.endsWith(".class")) {//说明是类文件
//通过类加载器获取来类文件的 Clazz 对象
// 先 得 到 类 的 完 整 类 路 径 形 式 为 com.hspedu.spring.component.MonsterService
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\")
+ 1, fileAbsolutePath.indexOf(".class"));
String classFullPath = path.replace("/", ".") + "." + className;
System.out.println("类名 = " + className);
System.out.println("类的全路径 = " + classFullPath);
try {
//获取到扫描包下的类的 clazz 对象```
Class<?> clazz = classLoader.loadClass(classFullPath);
if (clazz.isAnnotationPresent(Component.class)) {
//如果这个类有@Commponent, 说明是一个 spring bean
System.out.println("是一个 bean = " + clazz);
//老韩解读
//1. 因为这里不能直接将 bean 实例放入 singletonObjects
//2. 原因是如果 bean 是prototype是需要每次创建新的 bean对象
//3. 所以,Spring 底层是这样设计的: 将 bean 信息封装到BeanDefinition 对象中, 便于 getBean 的操作
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//获取 bean 的 name
Component componentAnnotation =
clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
if ("".equals(beanName)){ // 如果没有指定value
// 将该类的首字符小写作为beanname
beanName = StringUtils.uncapitalize(className);
}
//获取 bean 的 scope
if (clazz.isAnnotationPresent(Scope.class)) { // 如 果 有@Scope
Scope scopeAnnotation =
clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else { //如果没有@Scope, 默认是 singleton
beanDefinition.setScope("singleton");
}
//放入到 beanDefinitionMap
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果这个类没有@Commponent, 说明不是一个 spring bean
System.out.println("不是一个 bean = " + clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("=======================================");
}
}
}
}
public Object getBean(String name) {
return null;
}
}
步骤5 测试
package com.yb.spring;
import com.yb.spring.ioc.HspSpringApplicationContext;
import com.yb.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) {
//创建我们的 spring 容器对象
HspSpringApplicationContext hspSpringApplicationContext = new HspSpringApplicationContext(HspSpringConfig.class);
}
}
实现任务阶段 3
初始化 bean 单例池,并完成 getBean 方法 , createBean 方法

步骤 1 : com.yb.spring.ioc.HspSpringApplicationContext 添加相应的代码
package com.yb.spring.ioc;
import com.yb.spring.annotation.Component;
import com.yb.spring.annotation.ComponentScan;
import com.yb.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
public class HspSpringApplicationContext {
private Class configClass;
//如果 bean 是单例的,就直接放在这个 单例 bean 对象池
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
//将 bean 的定义,放在这个 beanDefinitionMap 集合
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public HspSpringApplicationContext(Class configClass) {
//通过扫描,得到 beanDefinition 的 map
beanDefinitionsByscan(configClass);
System.out.println(beanDefinitionMap);
//通过 beanDefinitionMap , 初始化 singletonObjects bean 单列池
Enumeration<String> keys = beanDefinitionMap.keys();
while (keys.hasMoreElements()) {
//得到 beanName
String beanName = keys.nextElement();
//通过 beanName 得到 beanDefinition
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//将该 bean 实例放入 singletonObjects
Object bean = createBean(beanDefinition);
singletonObjects.put(beanName, bean);
}
}
System.out.println("singletonObjects 单例池 = " + singletonObjects);
}
private void beanDefinitionsByscan(Class configClass) {
this.configClass = configClass;
//1. 解析配置类
//2. 获取到配置类的 @ComponentScan("com.hspedu.spring.component")
ComponentScan componentScan = (ComponentScan)
this.configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
System.out.println("扫描路径 = " + path);
//3. 获取扫描路径下所有的类文件
//(1) 先得到类加载器, 使用 App 方式来加载.
ClassLoader classLoader = HspSpringApplicationContext.class.getClassLoader();
//在获取某个包的 d 对应的 URL 时,要求是 com/hspedu/spring/component
//URL resource = classLoader.getResource("com/hspedu/spring/component");
//(2) 将 path 转成 形式为 com/hspedu/spring/component``
path = path.replace(".", "/");
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
System.out.println("=======================================");
System.out.println("文件绝对路径 = " + fileAbsolutePath);
if (fileAbsolutePath.endsWith(".class")) {//说明是类文件
//通过类加载器获取来类文件的 Clazz 对象
// 先 得 到 类 的 完 整 类 路 径 形 式 为 com.hspedu.spring.component.MonsterService
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\")
+ 1, fileAbsolutePath.indexOf(".class"));
String classFullPath = path.replace("/", ".") + "." + className;
System.out.println("类名 = " + className);
System.out.println("类的全路径 = " + classFullPath);
try {
//获取到扫描包下的类的 clazz 对象```
Class<?> clazz = classLoader.loadClass(classFullPath);
if (clazz.isAnnotationPresent(Component.class)) {
//如果这个类有@Commponent, 说明是一个 spring bean
System.out.println("是一个 bean = " + clazz);
//老韩解读
//1. 因为这里不能直接将 bean 实例放入 singletonObjects
//2. 原因是如果 bean 是prototype是需要每次创建新的 bean对象
//3. 所以,Spring 底层是这样设计的: 将 bean 信息封装到BeanDefinition 对象中, 便于 getBean 的操作
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//获取 bean 的 name
Component componentAnnotation =
clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
if ("".equals(beanName)) { // 如果没有指定value
// 将该类的首字符小写作为beanname
beanName = StringUtils.uncapitalize(className);
}
//获取 bean 的 scope
if (clazz.isAnnotationPresent(Scope.class)) { // 如 果 有@Scope
Scope scopeAnnotation =
clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else { //如果没有@Scope, 默认是 singleton
beanDefinition.setScope("singleton");
}
//放入到 beanDefinitionMap
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果这个类没有@Commponent, 说明不是一个 spring bean
System.out.println("不是一个 bean = " + clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("=======================================");
}
}
}
}
private Object createBean(BeanDefinition beanDefinition) {
//得到 bean 的类型
Class clazz = beanDefinition.getClazz();
try {
//使用反射得到实例
Object instance = clazz.getDeclaredConstructor().newInstance();
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//如果没有创建成功,返回 null
return null;
}
public Object getBean(String name) {
if (beanDefinitionMap.containsKey(name)) {
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
//得到 bean 的 scope , 分别处理
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//单例,直接从 bean 单例池获取
return singletonObjects.get(name);
} else { //不是单例,则没有返回新的实例
return createBean(beanDefinition);
}
} else {
throw new NullPointerException("没有该 bean");
}
}
}
测试
package com.yb.spring;
import com.yb.spring.component.MonsterDao;
import com.yb.spring.component.MonsterService;
import com.yb.spring.ioc.HspSpringApplicationContext;
import com.yb.spring.ioc.HspSpringConfig;
public class AppMain {
public static void main(String[] args) {
//创建我们的 spring 容器对象
HspSpringApplicationContext hspSpringApplicationContext = new HspSpringApplicationContext(HspSpringConfig.class);
MonsterService monsterService = (MonsterService)hspSpringApplicationContext.getBean("monsterService");
MonsterService monsterService2 = (MonsterService)hspSpringApplicationContext.getBean("monsterService");
System.out.println("monsterService"+monsterService);
System.out.println("monsterService2"+monsterService2);
MonsterDao monsterDao = (MonsterDao)hspSpringApplicationContext.getBean("monsterDao");
MonsterDao monsterDao2 = (MonsterDao)hspSpringApplicationContext.getBean("monsterDao");
System.out.println("monsterDao"+monsterDao);
System.out.println("monsterDao2"+monsterDao2);
}
}
实现任务阶段 4
完成依赖注入

步骤1 : 创建com.yb.spring.annotation.Autowired
package com.yb.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface Autowired {
//一个属性 required ,这里我们就不讲了,也比较简单, 有兴趣同学们作为课后加入
//String required() default "true";
}
步骤2 : 修改 com.yb.spring.component.MonsterDao
package com.yb.spring.component;
import com.yb.spring.annotation.Component;
@Component("monsterDao")
public class MonsterDao {
public void hi() {
System.out.println("hi 我是 monster Dao, select * from ....");
}
}
步骤3 : 修改com.yb.spring.component.MonsterService
package com.yb.spring.component;
import com.yb.spring.annotation.Autowired;
import com.yb.spring.annotation.Component;
import com.yb.spring.annotation.Scope;
/**
* 1. 使用@Component("monsterService") 修饰
* 2. 给该 MonsterService 注入到容器设置 beanName 为 monsterService
* 3. 如果没有设置,默认可以以类名 首字母小写来玩(底层还有很多细节)
*/
@Component(value = "monsterService")
@Scope("prototype")
public class MonsterService {
@Autowired
private MonsterDao monsterDao;
public void m1() {
//调用 monsterDao 的 hi()
monsterDao.hi();
}
}
步骤 4 : 修改 com.yb.spring.ioc.HspSpringApplicationContext
private Object createBean(BeanDefinition beanDefinition) {
//得到 bean 的类型
Class clazz = beanDefinition.getClazz();
try {
//使用反射得到实例
Object instance = clazz.getDeclaredConstructor().newInstance();
//完成依赖注入
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.isAnnotationPresent(Autowired.class)) {
// 处理@Autowired 注解的属性 required, 很简单,自己完成
// Autowired annotation =
declaredField.getAnnotation(Autowired.class);
// System.out.println(annotation.required());
//如果该属性有@Autowired, 就进行组装
Object bean = getBean(declaredField.getName());
declaredField.setAccessible(true);//因为属性是 private,需要暴破
declaredField.set(instance, bean);
}
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//如果没有创建成功,返回 null
return null;
}
测试
public class AppMain {
public static void main(String[] args) {
//创建我们的 spring 容器对象
HspSpringApplicationContext hspSpringApplicationContext = new HspSpringApplicationContext(HspSpringConfig.class);
MonsterService monsterService = (MonsterService) hspSpringApplicationContext.getBean("monsterService");
monsterService.m1();
}
}
实现任务阶段 5
bean 后置处理器实现
实现自己的 bean 后置处理器

步骤1 :定义接口com.yb.spring.process.InitializingBean
package com.yb.spring.process;
public interface InitializingBean {
// 实现该接口的 Bean , 需要重写此方法,此方法就是init方法
void afterPropertiesSet() throws Exception;
}
步骤2 : MonsterService.java, 实现 InitializingBean 接口
package com.yb.spring.component;
import com.yb.spring.annotation.Autowired;
import com.yb.spring.annotation.Component;
import com.yb.spring.annotation.Scope;
import com.yb.spring.process.InitializingBean;
/**
* 1. 使用@Component("monsterService") 修饰
* 2. 给该 MonsterService 注入到容器设置 beanName 为 monsterService
* 3. 如果没有设置,默认可以以类名 首字母小写来玩(底层还有很多细节)
*/
@Component(value = "monsterService")
@Scope("prototype")
public class MonsterService implements InitializingBean {
@Autowired
private MonsterDao monsterDao;
public void m1() {
//调用 monsterDao 的 hi()
monsterDao.hi();
}
// 1.afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
// 2即就是初始化方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MonsterService 进行初始化, 具体业务由程序员来搞定....");
}
}