智慧团建网站pc端,徐州集团网站建设方案,极速网站建设,抚州市建设局招标办网站文章目录 Spring(轻量级容器框架)Spring 学习的核心内容-一图胜千言IOC 控制反转 的开发模式Spring快速入门Spring容器剖析手动开发- 简单的 Spring 基于 XML 配置的程序课堂练习 Spring 管理 Bean-IOCSpring 配置/管理 bean 介绍Bean 管理包括两方面: Bean 配置方式基于 xml 文… 文章目录 Spring(轻量级容器框架)Spring 学习的核心内容-一图胜千言IOC 控制反转 的开发模式Spring快速入门Spring容器剖析手动开发- 简单的 Spring 基于 XML 配置的程序课堂练习 Spring 管理 Bean-IOCSpring 配置/管理 bean 介绍Bean 管理包括两方面: Bean 配置方式基于 xml 文件配置方式通过类型来获取 bean通过构造器配置 bean通过 p 名称空间配置 bean引用/注入其它 bean 对象引用/注入集合/数组类型通过 util 名称空间创建 list级联属性赋值通过静态工厂获取bean对象通过实例工厂获取对象通过 FactoryBean 获取对象(重点)★通过配置信息(继承)配置beanbean 对象的创建顺序bean对象的单例和多例bean的生命周期配置 bean 的后置处理器配置bean的后置处理器★通过属性文件给 bean 注入值基于 XML 的 bean 的自动装配 基于注解方式 ★自动装配AutoWired的自动装配Resource的自动装配 泛型依赖注入 AOP 切面编程动态代理Spring-AOP介绍● AOP 实现方式AOP 快速入门● 说明说明 AOP-切入表达式AOP-JoinPointAOP-返回通知获取结果AOP-异常通知中获取异常AOP-环绕通知【了解】AOP-切入点表达式重用AOP-切面优先级问题AOP-基于 XML 配置 AOP Spring(轻量级容器框架)
Spring 学习的核心内容-一图胜千言 1、Spring 核心学习内容 IOC、AOP, jdbcTemplate, 声明式事务 2、IOC: 控制反转 , 可以管理 java 对象 3. AOP : 切面编程 4. 4. JDBCTemplate : 是 spring 提供一套访问数据库的技术, 应用性强相对好理解 5. 声明式事务: 基于 ioc/aop 实现事务管理, 理解有需要小伙伴花时间 6. IOC, AOP 是重点同时难点
IOC 控制反转 的开发模式
程序-----容器 //容器创建好对象程序直接使用. 上图中 1、Spring 根据配置文件 xml/注解, 创建对象 并放入到容器(ConcurrentHashMap)中, 并且可以完成对象之间的依赖 2、当需要使用某个对象实例的时候, 就直接从容器中获取即可 3、程序员可以更加关注如何使用对象完成相应的业务, (以前是 new … 注解/配置方式) 4. DI—Dependency Injection 依赖注入可以理解成是 IOC 的另外叫法. 5. Spring 最大的价值通过配置给程序提供需要使用的web 层[Servlet(Action/Controller)]/Service/Dao/[JavaBean/entity] 对象, 这个是核心价值所在也是 ioc 的具体体现, 实现解耦. Spring快速入门 通过 Spring 的方式[配置文件]获取 JavaBean: Monster 的对象并给该的对象属性赋 值输出该对象信息. 下载Spring安装包创建 Java 工程spring5 , 为了清晰 Spring5 的各 jar 包作用老师使用 Java 工程引入开发 spring5 的基本包 配置Xml文件
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd!--1.配置Monster对象/JavaBean2.在beans中可以配置多个beanbean表示一个对象3. class属性是用于指定类的全路径 - spring底层使用反射创建4. id属性表示该java对象在spring容器中的id 将来通过id可以获取到该对象--bean classcom.spring.bean.Monster idmonster01property nameid value100/property namename value牛魔王/property nameskill value芭蕉扇//bean
/beans编写JavaBean
public class Monster {private int id;private String name;private String skill;// 无参构造器一定要给Spring反射创建对象时需要使用。public Monster() {}public Monster(int id, String name, String skill) {this.id id;this.name name;this.skill skill;}public int getId() {return id;}public void setId(int id) {this.id id;}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;}
}
编写Test Testpublic void getMonster() {// 1. 创建与配置文件关联的容器 ApplicationContext// 2.该容器和容器配置文件关联ApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);// 3. 根据xml里bean的id可以通过getBean获取对应的对象// 此时默认返回的是Object但是运行类型是Monster//Object monster01 ioc.getBean(monster01);Monster monster01 (Monster) ioc.getBean(monster01); // 可以直接强转, 此时可以调用里面的属性. monster01.getName()// 也可以在获取的时候,直接指定Class 类型Monster monster ioc.getBean(monster, Monster.class);// 4. 输出看效果System.out.println(monster01 运行类型 monster01.getClass());}
解释一下类加载路径
// 获取类加载的路径
// File f new File(this.getClass().getResource(/).getPath());
// System.out.println(f);debug 看看 spring 容器结构/机制, 记住你是 OOP 程序员,重要! 截图 debug的数据显示方式配置截图
Spring容器剖析 流程方式 查看容器注入了哪些 bean 对象,会输出 bean 的 id String[] str ioc.getBeanDefinitionNames();
// for (String string : str) {
// System.out.println(... string);
}底层图:
手动开发- 简单的 Spring 基于 XML 配置的程序 需求说明 自己写一个简单的 Spring 容器, 通过读取 beans.xml获取第 1 个 JavaBean: Monster 的对象并给该的对象属性赋值放入到容器中, 输出该对象信息也就是说不使用 Spring 原生框架我们自己简单模拟实现 本质就是两部分组成: 解析XML 反射
导入dom4j
/*** Author: GQLiu* DATE: 2024/1/16 10:18* 用于实现Spring的一个简单容器机制* 这里做的是解析xml文件, 拿到里面的属性值.*/
public class LgqApplicationContext {// 1. 编写一个单例对象池SingletonObjectConcurrentHashMapString, Object SingletonObject new ConcurrentHashMap();// 构造器// 接收一个容器的配置文件, 就是xml文件, 要保证该文件默认在srcpublic LgqApplicationContext(String iocBeanXmlFile) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {// 获取类加载路径String path this.getClass().getResource(/).getPath(); // /E:/JavaCode/Spring/out/production/Spring/// 创建SaxreaderSAXReader saxReader new SAXReader();// 得到Document对象Document document saxReader.read(new File(path iocBeanXmlFile));// 获取rootElement对象Element rootElement document.getRootElement();System.out.println(rootElement.elements()); // rootElement.elements() 表示根标签下的所有标签,是一个list// 得到第一个bean-monster01Element bean (Element) rootElement.elements(bean).get(0); // 获取bean标签的list的索引为0的Element元素// System.out.println(bean);String classFullPath bean.attributeValue(class); // 得到类全路径String id bean.attributeValue(id); // 获取idListElement property bean.elements(property); // 获取bean下的property属性列表, 转成Element类型.// 遍历 这里直接根据下标直接获取Integer 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 name skill);System.out.println(classFullPath);System.out.println(当前的id是? id);// 使用反射创建对象// 首先根据全类名获取Class对象Class? clazz Class.forName(classFullPath);// 使用newInstance()创建对象实例Monster monster (Monster) clazz.newInstance();// 创建完对象实例后,需要对属性值进行赋值monster.setId(monsterId);monster.setName(name);monster.setSkill(skill);System.out.println(monster);// 将Monster放入到SingletonObjects中this.SingletonObject.put(id,monster);}public Object getBean(String id) {return this.SingletonObject.get(id);}
}
课堂练习 ● 课堂练习 (10-15min): 创建一个 Car 类(id , name , price ), 具体要求如下: 创建 ioc 容器文件(配置文件)并配置一个 Car 对象(bean) .通过 java 程序到 ioc 容器获取该 bean 对象输出 Car.xml?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean classcom.spring.bean.Car idFerrari01property nameid value0001/property namename valueFerrari/property nameprice value999999999//bean
/beans/*** Author: GQLiu* DATE: 2024/1/16 13:27*/
public class homework02 {public static void main(String[] args) {// 根据配置文件创建容器ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(car.xml);Car car ioc.getBean(Ferrari01, Car.class); // 根据id获取对象实例System.out.println(car);}
}
Spring 管理 Bean-IOC
Spring 配置/管理 bean 介绍
Bean 管理包括两方面:
创建 bean 对象给 bean 注入属性
Bean 配置方式
基于 xml 文件配置方式
通过类型来获取 bean
就是通过类的class属性.获取类的对象实例.
按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个, 否则会抛出异常NoUniqueBeanDefinitionException这种方式的应用场景比如 XxxAction/Servlet/Controller, 或 XxxService 在一个线程 中只需要一个对象实例(单例)的情况 配置xml
bean idmonster01 classcom.hspedu.spring.beans.Monster
property namemonsterId value1/
property namename value牛魔王/
property nameskill value牛魔王拳/
/beanTestpublic void getMonsterByType() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);//1. 按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个, 否则会抛出异常NoUniqueBeanDefinitionException//2. 这种方式的应用场景比如 XxxAction/Servlet/Controller, 或 XxxService 在一个线程中只需要一个对象实例(单例)的情况Monster monster ioc.getBean(Monster.class);System.out.println(monster);}通过构造器配置 bean
配置xml!--通过构造器获取bean对象--bean classcom.spring.bean.Monster idMonster02!--1. 通过 index 属性来区分是第几个参数2. 通过 type 属性来区分是什么类型(按照顺序)3. 通过name属性指定是哪个变量.--constructor-arg nameid value626/constructor-arg namename value蜘蛛精/constructor-arg nameskill value吐丝//bean
//通过构造器配置 beanTestpublic void getMonsterByConstructor() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);Monster monster ioc.getBean(Monster02, Monster.class);System.out.println(monster);}通过 p 名称空间配置 bean
在 spring 的 ioc 容器, 可以通过 p 名称空间来配置 bean 对象
在 beans.xml 配置, 增加命名空间配置: 配置xml!--通过 p 名称空间配置 bean--bean classcom.spring.bean.Monster idMonster03p:id4p:name红孩儿p:skill三味真火/// 通过p名称空间配置beanTestpublic void getMonsterByP_Label() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);Monster monster ioc.getBean(Monster03, Monster.class);System.out.println(monster);}引用/注入其它 bean 对象
在 spring 的 ioc 容器, 可以通过 ref 来实现 bean 对象的相互引用
配置xml!--引用/注入其它 bean 对象通过 ref实现bean对象的相互引用--bean idmemberDAOImpl classcom.spring.dao.MemberDAOImpl/bean idmemberServiceImpl classcom.spring.service.MemberServiceImplproperty namememberDAO refmemberDAOImpl//bean// 通过注解方式实现对象引用// 在service类中是没有dao对象实例的,但是通过spring方法可以调用dao对象实例的方法, 因为spring框架给我们自动创建了dao对象.Testpublic void getMonsterByRef(){ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);MemberServiceImpl memberServiceImpl ioc.getBean(memberServiceImpl, MemberServiceImpl.class);memberServiceImpl.add();}引用/注入集合/数组类型 应用实例 创建Monster类和Master主人类
Properties 类 是 Hashtable 的子类 , 是 key-value 的形式存储的.Master.java
这里只写属性了private String name;private ListMonster monsterList;private MapString, Monster monsterMap;private SetMonster monsterSet;private String[] monsterName;//这个 Properties 是 Hashtable 的子类 , 是 key-value 的形式//这里 Properties key 和 value 都是 Stringprivate Properties pros;
编写beans.xml, 给集合, 数组, map properties 赋值
!--配置Master bean--
bean classcom.spring.bean.Master idmaster!--为Master 的 name 赋值--property namename value太上老君/!--给bean对象的list集合赋值--property namemonsterListlistref beanmonster01/ref beanMonster02//list/property!--给bean对象的map集合赋值--property namemonsterMapmapentrykey !-- key就是map的k --valuemonster03/value !--这里的value是字符串表示--/key!--这里的value是引用的bean--ref beanMonster03//entryentrykeyvaluemonster02/value/keyref beanMonster02//entry/map/property!--给bean对象的set集合赋值--property namemonsterSetsetref beanmonster01/ref beanMonster02//set/property!--给数组属性赋值--property namemonsterNamearrayvalue小妖怪/value !--直接给value赋值就是直接给数组赋值--value大怪兽/value/array/property!--给Properties属性赋值 结构k(String) - v(String)--property nameprospropsprop keyusernameroot/propprop keypwd123456/propprop keyip127.0.0.1/prop/props/property
bean/// 测试Testpublic void SetCollectionByPro() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);Master master ioc.getBean(master, Master.class);System.out.println(master);}通过 util 名称空间创建 list
编写JavaBean BookStorepublic class BookStore {private ListString book;public BookStore() {}public ListString getBook() {return book;}public void setBook(ListString book) {this.book book;}Overridepublic String toString() {return BookStore{ book book };}
}配置util BookStore util:list idbooksvalue红楼梦/valuevalue三国演义/valuevalue水浒传/valuevalue西游记/value/util:listbean idbookStore classcom.spring.bean.BookStoreproperty namebook refbooks//bean编写测试方法:
// 测试 util 名称空间Testpublic void getListByUtil() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);BookStore bookStore ioc.getBean(bookStore, BookStore.class);System.out.println(bookStore);}级联属性赋值
雇员Emp有姓名name和部门dept,dept是Dept类型的. 部门Dpet类有一个属性, 是name, 表示部门名称.
在beans.xml中配置部门和雇员!--配置Dept对象--bean classcom.spring.bean.Dept iddept/!--配置Emp对象--bean classcom.spring.bean.Emp idempproperty namename valuejack/property namedept refdept/!--这里我们希望给dept的name属性指定值[级联属性赋值]--property namedept.name value销售部门//bean// 编写测试用例// 测试属性的级联赋值Testpublic void setBeanByRelation() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);Emp emp ioc.getBean(emp, Emp.class);System.out.println(emp);}通过静态工厂获取bean对象
// beans.xml中配置静态工厂的参数说明
!--配置monster对象, 通过静态工厂获取--!--1. 通过静态工厂获取/配置bean2. class是静态工厂的全路径3. factory-method 表示 是指定静态工厂类的 哪个方法 返回我们的对象4. constructor-arg 中value指定要返回静态工厂的 哪个对象--bean idStaticFactory classcom.spring.factory.MyStaticFactory factory-methodgetMonsterconstructor-arg valuemonster02//bean// 编写静态工厂
/*** Author: GQLiu* DATE: 2024/1/17 10:56* 静态工厂类 可以返回一个Monster对象*/
public class MyStaticFactory {private static MapString, Monster monsterMap;// 使用static代码块进行初始化// 随着类加载的执行而执行只会执行一次。不会随着类的加载而执行。static {monsterMap new HashMap();monsterMap.put(monster01, new Monster(100, 牛魔王, 芭蕉扇));monsterMap.put(monster02, new Monster(200, 狐狸精, 美人计));}public MyStaticFactory() {}public static Monster getMonster(String id) {return monsterMap.get(id);}
}
// 测试通过静态工厂获取bean对象
// 通过静态工厂获取beanTestpublic void getBeanByStaticFactory() {ApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);Monster monster ioc.getBean(StaticFactory, Monster.class);System.out.println(monster);}通过实例工厂获取对象
实例工厂类
public class MyInstanceFactory {private MapString, Monster monsterMap;{monsterMap new HashMap();monsterMap.put(monster088, new Monster(300, 牛魔王, 芭蕉扇));monsterMap.put(monster099, new Monster(400, 狐狸精, 美人计));}public MyInstanceFactory() {}public Monster getMonster(String key){return this.monsterMap.get(key);}
}!--配置实例工厂对象--bean classcom.spring.factory.MyInstanceFactory idmyInstanceFactory/!--配置monster对象通过实例工厂获取--!--1. factory-bean表示指定使用哪个实例工厂返回bean2. factory-method表示指定实例工厂的哪个方法返回bean3. constructor-arg valuemonster099 表示获取到实例工厂中的哪个实例--bean idInstanceFactory factory-beanmyInstanceFactory factory-methodgetMonsterconstructor-arg valuemonster099//bean// 测试// 通过实例工厂获取beanTestpublic void getBeanByInstanceFactory() {ApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);Monster monster ioc.getBean(InstanceFactory, Monster.class);Monster monster2 ioc.getBean(InstanceFactory, Monster.class);System.out.println(monster); // Monster{id400, name狐狸精, skill美人计}System.out.println(monster monster2); // true// true是因为实例工厂都是用的一个所以里面的monster对象也都是从一个实例工厂的getMonster获取的(也就是HashMap一样)。所以monstermonster2// 这里如果生成 2 个实例工厂那就不是true而是false了// 如果是静态工厂就算生成了 2 个静态工厂那产生的monster也是一样的。}通过 FactoryBean 获取对象(重点)★
创建FactoryBean类 通过实现FactoryBean?接口来创建FactoryBean对象.
/*** Author: GQLiu* DATE: 2024/1/20 16:57*/
public class MyFactoryBean implements FactoryBeanMonster {// 这里的key就是配置时要根据key获取对应对象的那个keyprivate String key;private MapString, Monster monsterMap;{monsterMap new HashMap();monsterMap.put(monster088, new Monster(500, 牛魔王, 芭蕉扇));monsterMap.put(monster099, new Monster(600, 狐狸精, 美人计));}public String getKey() {return key;}public void setKey(String key) {this.key key;}// getObject就是获取Map中的指定上面private String key的对象Overridepublic Monster getObject() throws Exception {return this.monsterMap.get(key);}// 返回class类.Overridepublic Class? getObjectType() {return Monster.class;}Overridepublic boolean isSingleton() {return true; //单例的。}
}
在xml中配置FactoryBean
!--配置FactoryBean--bean idFactoryBean classcom.spring.factory.MyFactoryBeanproperty namekey valuemonster088//bean编写测试用例:
// 通过FactoryBean获取beanTestpublic void getBeanByFactoryBean() {ApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);Monster monster ioc.getBean(FactoryBean, Monster.class);System.out.println(monster);}就相当于多套了一层壳, key是通过在类里指定的方式获取的, 前面都是根据构造器的参数获取的:constructor-arg valuemonster099/, 这里是通过指定类中 的key.
通过配置信息(继承)配置bean
parent 表示继承自哪个bean实例bean idmonster8 classcom.spring.bean.Monster parentmonster/abstract表示这个bean是抽象的,不能实例化.bean idmonster classcom.spring.bean.Monster abstracttrue
bean 对象的创建顺序
在默认情况下, bean创建的顺序是按照配置顺序来的.但是如果我们增加depends-ondepartment的javabean, 那就会先去创建department的javabean, 然后再去创建当前的javabean. bean对象的单例和多例
单例: scope singleton
多例: scope prototype使用细节
默认是单例 singleton, 在启动容器时, 默认就会创建 , 并放入到 singletonObjects 集合当 设置为多实例机制后, 该 bean 是在 getBean()时才创建如 果 是 单 例 singleton, 同 时 希 望 在 getBean 时 才 创 建 , 可 以 指 定 懒 加 载lazy-init“true” (注意默认是 false)通常情况下, lazy-init 就使用默认值 false , 在开发看来, 用空间换时间是值得的, 除非有特殊的要求.如果 scope“prototype” 这时你的 lazy-init 属性的值不管是 ture, 还是 false 都是在getBean 时候才创建对象. bean idmonster10 classcom.spring.bean.Monster lazy-inittrue scopeprototype/bean的生命周期
bean的创建和销毁 init-method destroy-method
● 说明: bean 对象创建是由 JVM 完成的然后执行如下方法
执行构造器执行 set 相关方法调用 bean 的初始化的方法需要配置使用 bean当容器关闭时候调用 bean 的销毁方法需要配置
// 测试bean的生命周期Testpublic void testBeanTimeLine() {// 创建容器时会调用 init-method 方法.ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);House house ioc.getBean(house, House.class);// 关闭容器// 销毁容器时,会调用 destroy-method 方法ioc.close();}配置 bean 的后置处理器
● 说明
在 spring 的 ioc 容器,可以配置 bean 的后置处理器该处理器/对象会在 bean 初始化方法调用前和初始化方法调用后被调用程序员可以在后置处理器中编写自己的代码
配置bean的后置处理器★
在 spring 的 ioc 容器,可以配置 bean 的后置处理器该处理器/对象会在 bean 初始化方法调用前和初始化方法调用后被调用程序员可以在后置处理器中编写自己的代码可以对 IOC 容器中所有的对象进行统一处理 ,比如 日志处理/权限的校验/安全的验证/事务管理
首先创建后置处理器对象 创建类, 继承自BeanPostProcessor .实现 其两个方法: postProcessBeforeInitialization postProcessAfterInitialization
/*** Author: GQLiu* DATE: 2024/1/20 20:23* 配置bean的后置处理器*/
public class MyBeanPostProcessor implements BeanPostProcessor {/*** 什么时候创建: 在Bean的init方法前被调用** param bean 传入的IOC容器中创建/配置bean* param beanName 配置的bean的id* return 对返回的bean进行处理, 并返回.* throws BeansException*/Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 初步案例体验: 如果类型是House的, 统一改成name为北京豪宅// 对多个对象进行处理,就是切面编程if (bean instanceof House) {((House) bean).setName(北京豪宅);}System.out.println(postProcessBeforeInitialization bean bean beanname beanName);return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}/*** 么时候创建: 在Bean的init方法后被调用** param bean 传入的IOC容器中创建/配置bean* param beanName 配置的bean的id* return 对返回的bean进行处理, 并返回.* throws BeansException*/Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(postProcessAfterInitialization bean bean beanname beanName);return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}beans2.xml文件中配置bean和后置处理器 bean classcom.spring.bean.House idhouse init-methodinit destroy-methoddestroyproperty namename value大豪宅//bean!--配置后置处理器对象1. 当在beans2.xml配置文件中配置了MyBeanPostProcessor时,后置处理器的方法会被调用2. 修改会针对所有的对象, 所以是切面编程AOP--bean classcom.spring.bean.MyBeanPostProcessor idmyBeanPostProcessor/测试后置处理器 // 测试后置处理器的使用Testpublic void testBeanPostProcessor() {// 创建容器时会调用 init-method 方法.ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans2.xml);House house ioc.getBean(house, House.class);// 关闭容器// 销毁容器时,会调用 destroy-method 方法ioc.close();}通过属性文件给 bean 注入值
配置xml文件!--1. locationclasspath:my.properties 表示指定属性文件的位置2. classPath表示类路径.3. 这时我们的属性值通过${属性名}方式获取.--context:property-placeholder locationclasspath:my.properties/bean idmonster100 classcom.spring.bean.Monsterproperty nameid value${id}/property namename value${name}/property nameskill value${skill}//bean在src下编写my.properties属性文件
my.propertiesid998
namejack
skillhello// 测试// 通过属性文件给 bean 注入值Testpublic void setProByProFile() {// 创建容器时会调用 init-method 方法.ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);Monster monster ioc.getBean(monster100, Monster.class);System.out.println(monster); // Monster{id998, namejack, skillhello}// 关闭容器// 销毁容器时,会调用 destroy-method 方法ioc.close();}基于 XML 的 bean 的自动装配
在 spring 的 ioc 容器,可以实现自动装配 bean 自动装配有两种方式, 一种是byName, 一种是byType.
配置!--自动装配:1. autowirebyType 表示在创建orderService时, 通过类型给对象属性自动完成赋值/引用.2. 比如OrderService 对象有orderDAO属性,就会在容器中找有没有OrderDAO类型对象.如果有就会自动装配.3. 如果用byType 方式装配, 则容器中不能有相同类型的两个对象.4. 如果你的对象没有属性, autowire就没有必要写.********************************1. autowirebyName 表示通过名字完成装配2. Spring容器会先看OrderService属性, 再根据这个属性的setXxx()方法来找对象id. 如果没有就装配失败.--!--配置OrderDao对象--bean classcom.spring.dao.OrderDAO idorderDAO/!--配置OrderService对象--bean autowirebyName classcom.spring.service.OrderService idorderService/!--配置OrderAction对象--bean autowirebyType classweb.OrderAction idorderAction/
测试// 通过自动装配给对象赋值Testpublic void setBeanByAutowire() {// 创建容器时会调用 init-method 方法.ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans2.xml);OrderAction orderAction ioc.getBean(orderAction, OrderAction.class);System.out.println(orderAction);//验证是否装配上了OrderService;System.out.println(orderAction.getOrderService());// 验证是否装配上了OrderDAOSystem.out.println(orderAction.getOrderService().getOrderDao());// 关闭容器// 销毁容器时,会调用 destroy-method 方法ioc.close();}基于注解方式 ★
● 基本介绍 基于注解的方式配置 bean, 主要是项目开发中的组件比如 Controller、Service、和 Dao.
● 组件注解的形式有
Component 表示当前注解标识的是一个组件是一个通用性质的标识可以是controller、service、repository。Controller 表示当前注解标识的是一个控制器通常用于 ServletService 表示当前注解标识的是一个处理业务逻辑的类通常用于 Service 类Repository 表示当前注解标识的是一个持久化层的类通常用于 Dao 类 步骤 1… 引入 spring-aop-5.3.8.jar , 在 spring/libs 下拷贝即可 2.
!-- 配置自动扫描的包注意需要加入 context 名称空间 --
context:component-scan base-packagecom.hspedu.spring.component /// 测试用例
Test
public void getBeanByAnnotation() {
ApplicationContext ioc new ClassPathXmlApplicationContext(beans.xml);
UserAction userAction ioc.getBean(UserAction.class);
System.out.println(userAction);
UserDao userDao ioc.getBean(UserDao.class);
System.out.println(userDao);
MyComponent myComponent ioc.getBean(MyComponent.class);
System.out.println(myComponent);
UserService userService ioc.getBean(UserService.class);
System.out.println(userService);
}注意事项和细节说明
需要导入 spring-aop-5.3.8.jar , 别忘了必须在 Spring 配置文件中指定自动扫描的包IOC 容器才能够检测到当前项目中哪些类被标识了注解 注意到导入 context 名称空间
!-- 配置自动扫描的包 --
context:component-scan base-packagecom.hspedu.spring.component /
可以使用通配符 * 来指定 比如 com.hspedu.spring.* 表示–老韩提问: com.hspedu.spring.component 会不会去扫描它的子包? 答会的 3. Spring 的 IOC 容器不能检测一个使用了Controller 注解的类到底是不是一个真正的控制器。注解的名称是用于程序员自己识别当前标识的是什么组件。其它的ServiceRepository 也是一样的道理 [也就是说 spring 的 IOC 容器只要检查到注解就会生成对象但是这个注解的含义 spring 不会识别注解是给程序员编程方便看的] 4. resource-pattern表示只扫描满足要求的类[使用的少不想扫描不写注解就可以, 知道这个知识点即可]
context:component-scan base-packagecom.hspedu.spring.component resource-patternUser*.class/排除哪些类 , 以 annotaion 注解为例
context:component-scan base-packagecom.hspedu.spring.component context:exclude-filter typeannotation expressionorg.springframework.stereotype.Service/
/contextcontext:exclude-filter 放在 context:component-scan 内表示扫描过滤掉当前包的某些类type“annotation” 按照注解类型进行过滤.expression :就是注解的全类名比如org.springframework.stereotype.Service 就是Service 注解的全类名,其它比Controller Repository 等 依次类推上面表示过滤掉 com.hspedu.spring.component 包下加入了Service 注解的类完成测试 修改 beans.xml, 增加 exclude-filter , 发现 UserService, 不会注入到容器.
指定自动扫描哪些注解类
1. use-default-filtersfalse: 不再使用默认的过滤机制
2. context:include-filter: 表示只是扫描指定的注解的类
3. expressionorg.springframework.stereotype.Controller: 注解的全类名context:component-scan base-packagecom.hspedu.spring.component use-default-filtersfalse
context:include-filter typeannotation expressionorg.springframework.stereotype.Service/
context:include-filter typeannotation expressionorg.springframework.stereotype.Controller/
/context:component-scan
默认情况标记注解后类名首字母小写作为 id 的值。也可以使用注解的 value 属性 指定 id 值并且 value 可以省略。
Controller(valueuserAction01)
Controller(userAction01)指定的就是下面的idbean classcom.spring.dao.OrderDAO idorderDAO/ 自动装配
基于注解配置 bean也可实现自动装配使用的注解是AutoWired 或者 ResourceAutoWired 的规则说明 在 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 策略, 如果都不成功就会报错 老韩建议不管是Autowired 还是 Resource 都保证属性名是规范的写法就可以 注入
AutoWired的自动装配
配置xml 使用注解。
!--基于注解的配置指定自动扫描的包--context:component-scan base-packagecom.spring.component/UserAction/*** Author: GQLiu* DATE: 2024/1/21 11:19*/Controller
public class UserAction {// 下面的Autowired 表示自动装配Autowiredprivate UserService userService;public void sayok(){System.out.println(UserAction的sayok~);userService.hi();}
}使用Autowired也可以指定 id 进行组装
Autowired
Qualifier(value指定id)UserService Service
public class UserService {public void hi(){System.out.println(UserService的hi~);}
}
// 测试方法// 测试注解的自动装配Testpublic void AutowireByAnntation() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(bean3.xml);UserAction userAction ioc.getBean(UserAction.class);userAction.sayok();}结果
UserAction的sayok~
UserService的hi~注意事项和细节说明
如果在IOC容器中查找待装配的组件的类型时只有唯一的bean与之匹配则使用该bean装配。如待装配的类型对应的 bean 在 IOC 容器中有多个则使用待装配的属性的属性名作为 id 值再进行查找, 找到就装配找不到就抛异常
上面说的第二条 使用属性名作为id查询的意思是
public class UserAction {// 下面的Autowired 表示自动装配Autowiredprivate UserService userService; // userService就表示属性名public void sayok(){System.out.println(UserAction的sayok~);userService.hi();}
}
上面的代码中 userService就表示属性名 然后根据这个名字去匹配xml中javabean的id。因为不指定名字在使用注解创建bean时就是使用类名的首字母为小写作为bean的id。比如说有多个同类型的javabean一个javabean是配置的id为userService400, 然后javabean中有一个id为userService400的javabean那就用这个了。Resource的自动装配
Resource 的规则说明 Resource 有两个属性是比较重要的,分是 name 和 type,Spring 将Resource 注解的name 属性解析为 bean 的名字,而 type 属性则解析为 bean 的类型.所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略如果Resource 没有指定 name 和 type ,则先使用byName注入策略, 如果匹配不上,再使用 byType 策略, 如果都不成功就会报错
Controller
public class UserAction {//Autowired//Resource(name userService200) // 使用 bean的id自动装配Resource(type UserService.class) //按照类型实现自动装配private UserService userService;public void sayok(){System.out.println(UserAction的sayok~);userService.hi();}
}泛型依赖注入
为了更好的管理有继承和相互依赖的 bean 的自动装配spring 还提供基于泛型依赖的 注入机制在继承关系复杂情况下泛型依赖注入就会有很大的优越性 应用实例需求 希望在PhoneService中能够调用PhoneDao。这是通过在上面一层BaseService和BaseDao层面的依赖注入得到的。 public abstract class BaseDaoT {public abstract void save();
}public class BaseServiceT {Autowiredprivate BaseDaoT baseDao;public void save() {baseDao.save();}
}public class Book {
}Repository
public class BookDao extends BaseDaoBook{Overridepublic void save() {System.out.println(BookDao 的 save()..);}
}Service
public class BookService extends BaseServiceBook{//并没有写属性
}public class Phone {
}Repository
public class PhoneDao extends BaseDaoPhone{Overridepublic void save() {System.out.println(PhoneDao save());}
}Service
public class PhoneService extends BaseServicePhone{}下面使用注解方式对他们进行配置 context:component-scanbase-packagecom.spring.depinjection/下面测试泛型依赖注入 Testpublic void setProByDependencyInjection() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans7.xml);;PhoneService phoneService ioc.getBean(PhoneService.class);phoneService.save();}说明在调用phoneService时此时 T 里的泛型是phone所以BaseService T 及其里面的属性private BaseDao T baseDao 都是phone类型的。所以说他在调用save方法时首先会去着baseDao的save方法然后这个baseDao再根据泛型、动态绑定机制去找phoneDao的save方法。
AOP 切面编程
动态代理 ● 需求说明 有 Vehicle(交通工具接口, 有一个 run 方法), 下面有两个实现类 Car 和 Ship当运行 Car 对象 的 run 方法和 Ship 对象的 run 方法时输入如下内容, 注意观察前后有统一的输出. Vehicle接口
public interface Vehicle {public void run();public String fly(int height);
}Car.java:
public class Car implements Vehicle{Overridepublic void run() {System.out.println(小汽车在路上 running....);}Overridepublic String fly(int height) {System.out.println(小汽车可以飞翔 高度 height);return 小汽车可以飞翔 高度 height;}
}Ship.java:
public class Ship implements Vehicle{Overridepublic void run() {System.out.println(大轮船在水上 running....);}Overridepublic String fly(int height) {System.out.println(轮船可以飞翔 高度 height);return 轮船可以飞翔 高度 height;}
}
// 提供代理的Java类
VehicleProxyProvider 该类可以返回一个代理对象.import java.lang.reflect.Proxy;
public class VehicleProxyProvider {//定义一个属性//target_vehicle 表示真正要执行的对象//该对象实现了Vehicle接口private Vehicle target_vehicle;//构造器public VehicleProxyProvider(Vehicle target_vehicle) {this.target_vehicle target_vehicle;}//编写一个方法可以返回一个代理对象, 该代理对象可以通过反射机制调用到被代理对象的方法//老师解读//1. 这个方法非常重要 理解有一定难度public Vehicle getProxy() {//得到类加载器ClassLoader classLoader target_vehicle.getClass().getClassLoader();//得到要代理的对象/被执行对象 的接口信息,底层是通过接口来完成调用Class?[] interfaces target_vehicle.getClass().getInterfaces();//创建InvocationHandler 对象//因为 InvocationHandler 是接口所以我们可以通过匿名对象的方式来创建该对象/**** public interface InvocationHandler {* public Object invoke(Object proxy, Method method, Object[] args)* throws Throwable;* }* invoke 方法是将来执行我们的target_vehicle的方法时会调用到**/InvocationHandler invocationHandler new InvocationHandler() {/*** invoke 方法是将来执行我们的target_vehicle的方法时会调用到* param o 表示代理对象* param method 就是通过代理对象调用方法时的哪个方法 代理对象.run()* param args : 表示调用 代理对象.run(xx) 传入的参数* return 表示 代理对象.run(xx) 执行后的结果.* throws Throwable*/Overridepublic Object invoke(Object o, Method method, Object[] args)throws Throwable {System.out.println(交通工具开始运行了....);//这里是我们的反射基础 OOP//method 是: public abstract void com.hspedu.spring.proxy2.Vehicle.run()//target_vehicle 是? Ship对象//args 是null//这里通过反射动态绑定机制就会执行到被代理对象的方法//执行完毕就返回Object result method.invoke(target_vehicle, args);System.out.println(交通工具停止运行了....);return result;}};/*public static Object newProxyInstance(ClassLoader loader,Class?[] interfaces,InvocationHandler h)老师解读1. Proxy.newProxyInstance() 可以返回一个代理对象2. ClassLoader loader: 类的加载器.3. Class?[] interfaces 就是将来要代理的对象的接口信息4. InvocationHandler h 调用处理器/对象 有一个非常重要的方法invoke*/Vehicle proxy (Vehicle)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}
// 无注释版
public class VehicleProxyProvider {//定义一个属性//target_vehicle 表示真正要执行的对象//该对象实现了Vehicle接口private Vehicle target_vehicle;//构造器public VehicleProxyProvider(Vehicle target_vehicle) {this.target_vehicle target_vehicle;}//编写一个方法可以返回一个代理对象, 该代理对象可以通过反射机制调用到被代理对象的方法//老师解读//1. 这个方法非常重要 理解有一定难度public Vehicle getProxy() {//得到类加载器ClassLoader classLoader target_vehicle.getClass().getClassLoader();//得到要代理的对象/被执行对象 的接口信息,底层是通过接口来完成调用Class?[] interfaces target_vehicle.getClass().getInterfaces();// 匿名内部类InvocationHandler invocationHandler new InvocationHandler() {Overridepublic Object invoke(Object o, Method method, Object[] args)throws Throwable {System.out.println(交通工具开始运行了....);Object result method.invoke(target_vehicle, args);System.out.println(交通工具停止运行了....);return result;}};Vehicle proxy (Vehicle)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}
// 测试使用动态代理public class TestVehicle {Testpublic void proxyRun() {//创建Ship对象Vehicle vehicle new Car();//创建VehicleProxyProvider对象, 并且我们传入的要代理的对象VehicleProxyProvider vehicleProxyProvider new VehicleProxyProvider(vehicle);//获取代理对象, 该对象可以代理执行方法//老师解读//1. porxy 编译类型 Vehicle//2. 运行类型 是代理类型 class com.sun.proxy.$Proxy9Vehicle proxy vehicleProxyProvider.getProxy();System.out.println(proxy的编译类型是 Vehicle);System.out.println(proxy的运行类型是 proxy.getClass());//下面老韩就要给大家解读/debug怎么 执行到 代理对象的 public Object invoke(Object o, Method method, Object[] args)//梳理完毕. proxy的编译类型是 Vehicle, 运行类型是 class com.sun.proxy.$Proxy9//所以当执行run方法时会执行到 代理对象的invoke//如何体现动态 [1. 被代理的对象 2. 方法]proxy.run();String result proxy.fly(10000);System.out.println(result result);}执行结果
执行流程
调用proxy.run()时会直接进入 new InvocationHandler这个匿名内部类中. 然后依次执行. 执行到Object result method.invoke(target_vehicle, args); 时, 会跑到这个target_vehicle对象中执行对应的方法. Spring-AOP介绍
底层是 ASPECTJAOP 的全称(aspect oriented programming) 面向切面编程 切面类就是切面编程这个类中的方法可以被其他类的任意方法在任意时间调用灵活度很高相当于在一个类的某个方法执行过程中某个切面时刻调用方法。
AOP的思路单独写一个类然后里面写很多静态方法然后在别的类里调用这个类里的静态方法的过程就是切面编程
● AOP 实现方式
基于动态代理的方式[内置 aop 实现]使用框架 aspectj 来实现
AOP 快速入门
● 说明
需要引入核心的 aspect 包在切面类中声明通知方法 前置通知Before返回通知AfterReturning异常通知AfterThrowing后置通知After环绕通知Around 我们使用 aop 编程的方式来实现手写的动态代理案例效果就以上一个案例为例来讲解 说明 SmartAnimalAspect 是一个切面类 所有的之前的切面方法放到这里去统一管理 SmartAnimalImplement是一个实例方法 运行的类在这个类运行前执行切面类中的某个切面方法(这里的f1)。
SmartAnimal 接口
/*** Author: GQLiu* DATE: 2024/1/22 21:14*/
public interface SmartAnimal {public int getSum(int a,int b);public int getSub(int a,int b);
}
切面类/*** Author: GQLiu* DATE: 2024/1/23 20:42* SmartAnimalAspect 作 用 就 是 去 接 管 切 面 编 程 , 此 时 原 来 的* MyProxyProvider 类就可以拿掉了.*/
Aspect // 表示是一个切面类[底层切面编程的支撑(动态代理反射动态绑定)]
Component // 将切面类注入SmartAnimalAspect到容器
public class SmartAnimalAspect {// 是写切面方法.// 在方法执行前执行的切面方法// 希望将f1方法切入到SmartAnimalImplement-getSum前执行/*** Before: 表示前置通知.即在我们的目标对象执行方法前执行.* value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int ,int))* 指定切入到哪个类的哪个方法 形式是 访问修饰符 返回类型 全类名.方法名(形参列表)* f1方法可以理解成一个切入方法,这个方法名可以是程序员指定 showBeginLog* param joinPoint : 在底层执行时, 由AspectJ切面框架会给该切入方法传入JoinPoint连接点对象.* 通过该参数, 可以获取到相关信息.*/Before(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)))public void showBeginLog(JoinPoint joinPoint) {// 通过连接点对象joinPoint 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showBeginLog()-方法执行前-日志-方法名- signature.getName() Arrays.asList(joinPoint.getArgs()));}//返回通知 :即把f2方法切入到目标独享方法正常执行后再执行。AfterReturning(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)))public void showSuccessEndLog(JoinPoint joinPoint) {// 通过连接点对象joinPoint 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showSuccessEndLog()-方法执行后-日志-方法名- signature.getName() Arrays.asList(joinPoint.getArgs()));}// 异常通知 将该方法切入到目标方法执行发生异常的catch{}AfterThrowing(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)))public void showExceptionLog(JoinPoint joinPoint) {// 通过连接点对象joinPoint 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showExceptionLog()-方法执行出现异常-日志-方法名- signature.getName() Arrays.asList(joinPoint.getArgs()));}// 最终通知将f4方法切入到目标方法执行后不管是否发生异常都执行After(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)))public void showFinallyEnd(JoinPoint joinPoint) {// 通过连接点对象joinPoint 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showFinallyEnd()-方法执行后-日志-方法名- signature.getName() Arrays.asList(joinPoint.getArgs()));}
}实例类在这个类的某个方法(这里是getSum, getSub)执行前(后),执行某个切面方法(如切面类SmartAnimalAspect 里的切面方法f1)
/*** Author: GQLiu* DATE: 2024/1/22 21:15*/
Component // 使用Component 当Spring容器启动时, 将SmartDog注入容器中
public class SmartAnimalImplement implements SmartAnimal {Overridepublic int getSum(int a, int b) {
// System.out.println(日志-方法名-getSum 参数 a b);return a b;}Overridepublic int getSub(int a, int b) {
// System.out.println(日志-方法名-getSum 参数 a b);return a - b;}
}细节说明
关于切面类方法命名可以自己规范一下, 比如 showBeginLog() . showSuccessEndLog() showExceptionLog(), showFinallyEndLog()切入表达式的更多配置比如使用模糊配置 Before(value“execution(* com.hspedu.aop.proxy.SmartDog.*(…))”)表示所有访问权限所有包的下所有有类的所方法都会被执行该前置通知方法 Before(value“execution(* .(…))”)当 spring 容器开启了 !-- 开启基于注解的 AOP 功能 -- aop:aspectj-autoproxy/ , 我们获 取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型 了!当 spring 容器开启了 !-- 开启基于注解的 AOP 功能 -- aop:aspectj-autoproxy/ , 我们获 取注入的对象, 也可以通过 id 来获取, 但是也要转成接口类型.
课后作业
配置文件不用动!--扫描com.spring.aop.aspectj这个包查找Spring组件--context:component-scanbase-packagecom.spring.aop.aspectj/!--开启基于注解的AOP功能--aop:aspectj-autoproxy/
切面类中加上这些Before(value execution(public void com.spring.aop.aspectj.Phone.work()) || execution(public void com.spring.aop.aspectj.Camera.work()))public void showBeginLog_Phone(JoinPoint joinPoint) {Signature signature joinPoint.getSignature();System.out.println(切面类shoBeginLog_Phone/Camera-方法执行前-日志-方法名 signature.getName());}phone和camera注意要加上ComponentComponent
public class Phone implements UsbInterface{Overridepublic void work() {System.out.println(手机Phone执行work。。。);}
}Component
public class Camera implements UsbInterface{Overridepublic void work() {System.out.println(相机Camera执行工作。。。);}
}
测试代码Testpublic void UsbInterfaceTest() {ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans8.xml);String[] beanNames ioc.getBeanDefinitionNames();for (String beanName : beanNames) {Object bean ioc.getBean(beanName);System.out.println(beanName : bean.getClass().toString());}// UsbInterface camera (UsbInterface) ioc.getBean(Camera.class); // 傻叉 这是代理类型的, 通过Camera类型拿肯定拿不到!// 这时可以通过类名首字母小写, 即通过类名的方式拿.UsbInterface camera (UsbInterface) ioc.getBean(camera);camera.work();System.out.println();}AOP-切入表达式 注意事项和细节
切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效切入表达式也可以对没有实现接口的类进行切入就是对普通的类的某个执行方法也可以进行前后的切入。
动态代理 jdk 的 Proxy 与 Spring 的 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-返回通知获取结果 // 修改切面类的方法//返回通知 :即把f2方法切入到目标独享方法正常执行后再执行。AfterReturning(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)),returning res)public void showSuccessEndLog(JoinPoint joinPoint, Object res) {// 通过连接点对象joinPoint 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showSuccessEndLog()-方法执行后-日志-方法名- signature.getName() Arrays.asList(joinPoint.getArgs()));System.out.println(返回通知的结果为 res); // 返回通知的结果为12}
AOP-异常通知中获取异常
如何在异常通知方法中获取异常信息
// 修改后的切面类// 异常通知 将该方法切入到目标方法执行发生异常的catch{}AfterThrowing(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)),throwing throwable)public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {// 通过连接点对象joinPoint 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showExceptionLog()-方法执行出现异常-日志-方法名- signature.getName() Arrays.asList(joinPoint.getArgs()));System.out.println(异常通知 -- 异常信息-- throwable);}AOP-环绕通知【了解】
环绕通知可以完成其它四个通知要做的事情
如何使用环绕通知完成其它四个通知的功能。 // 环绕通知Around(value execution(public int getSum(int, int)))public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {Object result null;String methodName joinPoint.getSignature().getName();try {// 1. 相当于完成前置通知Before完成的事Object[] args joinPoint.getArgs();ListObject list Arrays.asList(args);System.out.println(Aop环绕通知-- methodName 方法开始了 参数有 list);// 在环绕通知中一定要调用joinPoint.proceed()来执行目标方法result joinPoint.proceed();// 2. 相当于返回通知AfterReturning完成的事情System.out.println(Aop环绕通知 methodName 方法结束了--结果是 result);} catch (Throwable e) {// 3. 相当于异常通知完成的事儿System.out.println(Aop环绕通知 methodName 方法抛出异常了--异常对象 e);} finally {// 4. 相当于最终通知完成的事情System.out.println(Aop后置通知 methodName 方法最终结束了。);}return result;/*执行结果Aop环绕通知--getSum 方法开始了 参数有[10, 2]12方法内部打印res12Aop环绕通知getSum方法结束了--结果是12Aop后置通知getSum方法最终结束了。12*/}// 测试类Testpublic void smartAnimalImplementTest() {// 得到的spring容器ClassPathXmlApplicationContext ioc new ClassPathXmlApplicationContext(beans8.xml);// 这里需要通过接口类型获取到注入的SmartDog对象SmartAnimal smartAnimal ioc.getBean(SmartAnimal.class);int sub smartAnimal.getSum(10, 2);System.out.println(sub);System.out.println(SmartAnmial的运行类型 smartAnimal.getClass()); // class com.sun.proxy.$Proxy13}AOP-切入点表达式重用
对某个切入表达式如果很多都用到了某个切入表达式则就用这个切入表达式重用PointCut()来实现重用这个切入表达式。
AfterReturning(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)),returning res)Before(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)))上面这两个切入表达式的value值都是一样的都是一个类中的getSum方法。所以这里使用切入表达式重用技术来实现重用。// 切入表达式重用Pointcut(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)))public void myPointCut() {}// Before(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)))//使用切入表达式重用Before(value myPointCut())public void showBeginLog(JoinPoint joinPoint) {// 通过连接点对象joinPoint 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showBeginLog()-方法执行前-日志-方法名- signature.getName() Arrays.asList(joinPoint.getArgs()));}//返回通知 :即把f2方法切入到目标独享方法正常执行后再执行。// AfterReturning(value execution(public int com.spring.aop.aspectj.SmartAnimalImplement.getSum(int,int)),// returning res)AfterReturning(value myPointCut(), returning res)public void showSuccessEndLog(JoinPoint joinPoint, Object res) {// 通过连接点对象joinPoint 可以获取方法签名Signature signature joinPoint.getSignature();System.out.println(切面类showSuccessEndLog()-方法执行后-日志-方法名- signature.getName() Arrays.asList(joinPoint.getArgs()));System.out.println(返回通知的结果为 res); // 返回通知的结果为12}AOP-切面优先级问题
● 切面优先级问题: 如果同一个方法有多个切面类的不同切面方法在同一个切入点切入那么执行的优先级如何控制. ● 基本语法: order(valuen) 来控制 n 值越小优先级越高. 上面使用SmartAnimalAspect和SmartAnimalAspect3切面类去做然后根据这两个切面类的优先级去处理先后顺序问题。
注意事项和细节说明
不能理解成优先级高的每个消息通知都先执行这个和方法调用机制(和 Filter 过滤器链式调用类似) 如何理解执行顺序
AOP-基于 XML 配置 AOP
● 基本说明: 前面我们是通过注解来配置 aop 的在 spring 中我们也可以通过 xml 的方式来配置 AOP