Spring Xml 文件报红第一时间检查set 、get 、构造方法

准备工作

  • JDK 最低版本17
  • 设置Maven
    • 见[王鹤老师的笔记]
本套Spring教程与其他Spring教程的区别可总结为以下几点:
第一点:手写Spring框架
第二点:手写组件扫描器
第三点:依赖倒置原则DIP
第四点:CGLIB动态代

§1 Spring启示录

当前项目

dao
UserDao
impl
UserDaoImpl_1
service
UserService
impl
UserServiceimpl_1
web
UserAction service为了调用dao层方法,有dao层接口的实现类
web为了调用service层方法,有service层接口的实现类

缺点

若用户提出需求,程序员对项目的功能进行拓展时,就需要更改多层已经运行正常的的代码,不符合软件开发的“开闭原则”[OCP],即对扩展开放,对修改关闭

同时,也违背了“依赖倒置原则”

思想与解决方案

违背依赖倒置
上层依赖下层 : 上层因下层的改动而改动
[这样不好,违背依赖倒置原则!!!] 符合依赖倒置:
上不依赖下,"面向接口编程"
依赖倒置原则的目的,降低程序的耦合度,提高扩展力 解决思想:[控制反转]
只保留接口,把创建对象的的权利、以及对象的维护权利交出
- 重点:"让出权利"
- 出现的比较新,没有被纳入GoF23种设计模式中 解决方案:依赖注入[DI]
何如实现控制反转?
- 第一种:set注入(执行set方法给属性赋值)
- 第二种:构造方法注入(执行构造方法给属性赋值) 依赖:A对象和B对象的关系。
注入:是一种手段,通过这种手段,可以让A对象和B对象产生关系。
依赖注入:对象A和对象B之间的关系,靠注入的手段来维护。而注入包括:set注入和构造注入 控制反转是思想。依赖注入是这种思想的具体实现。
Spring是一个实现了IoC思想的容器。

注意术语:

术语 - - 全称
OCP 开闭原则 开发原则 Open Close Principle
DIP 依赖倒置原则 开发原则 Dependence Inversion Principle
IoC 控制反转思想 一种新型的设计模式 Inversion of Control
DI 依赖注入 控制反转思想的具体实现方式 Dependency Injection

§2 Spring 简述

SSM

Spring

SpringMVC

Mybatis

面向切面编程:[蓉姐]

AOP(Aspect Orient Programming),面向切面编程。

切面:公共的,通用的,重复的功能称为切面,面向切面编程就是将切面提取出来,单独开发,在需要调用的方法中通过动态代理的方式进行织入

1)切面:就是那些重复的,公共的,通用的功能称为切面,例如:日志,事务,权限.

2)连接点:就是目标方法.因为在目标方法中要实现目标方法的功能和切面功能.

3)切入点(Pointcut):指定切入的位置,多个连接点构成切入点.切入点可以是一个目标方法,可以是一个类中的所有方法,可以是某个包下的所有类中的方法.

4)目标对象:操作谁,谁就是目标对象.

5)通知(Advice):来指定切入的时机.是在目标方法执行前还是执行后还是出错时,还是环绕目标方法切入切面功能.

§3 Spring 入门程序

1、代码编程步骤

基本结构
	1-创建一个maven项目
补充项目结构
resources 并设置为 资源文件夹
同理补充测试资源文件夹 2-修改pom.xml文件
删除build标签
添加 spring框架需要的核心依赖 spring-context
如果没有jutil 包则添加
刷新pom.xml文件 Spring6未正式发布需要添加Spring提供的仓库地址
Add the following to resolve milestone and RC versions – for example, `6.0.0-M2` or `6.0.0-RC1`:
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>



当加入spring context的依赖之后,会关联引入其他依赖:

spring aop:面向切面编程

spring beans:IoC核心

spring core:spring的核心工具包

spring jcl:spring的日志包

spring expression:spring表达式

	3-创建项目所需的咖啡豆[无参构造、setXXX方法]
一定得有无参构造,底层使用的是反射机制通过无参构造创建对象 4-applicationContext.xml文件 - 配置文件放在resources类根路径下,可移植性高
- IDEA支持spring配置文件,有模板!!
- 在XML Configuration File → Spring Config
- xml的文件的名字随意
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--
<bean id="userBean" class="com.nfjh.spring6.bean.User">
</bean> id是bean的唯一标识,不能重复
class 是 对应bean类的全限定类名
-->
<bean id="" class="">
<property name="" value=""/>
<property name="" ref=""/>
<!--
㊟:value 为简单类型赋值
ref 为引用类型赋值,且要求当前bean工厂中存在该引用类型对象
ref中填写的是bean的id
-->
</bean> </beans>

㊟:value 为简单类型赋值
ref 为引用类型赋值,且要求当前bean工厂中存在该引用类型对象 5- 创建测试方法
创建工厂 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml路径");
//此处的构造方法是可变长参数,所以如果有多个可以直接加 /*此处有多个重载方法
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, (ApplicationContext)null);
} 若项目为普通项目,不是maven的,此处出现了错误
㊟:检查是否将resources 设置为资源文件夹
*/ //返回Object
User user = (User) applicationContext.getBean("userBean"); /*
另一种写法: 直接返回的就是我们需要的类型
在传入id的同时传入类型
User user = applicationContext.getBean("userBean", User.class);
*/

项目文件的修改与导入

	1、删除target文件夹
2、删除 .iml 文件
3、修改pom.xml文件中的坐标
<artifactId>MVC_SpringXml</artifactId>
修改为项目名
4、导入
Project Structure
→ Modules
→ +
→ import Modules
→ 选中项目的根路径
→ import module from external model
→ maven
→ 一直next即可

§4 Spring对IoC的实现

实现依赖注入

set注入是在对象创建之后执行

构造注入是在对象实例化的过程中执行的

set注入

  • javaBean中存在符合命名规范的set方法
  • applicationContext.xml文件中进行了配置
<bean id="userDaoBean" class="com.nfjh.spring6.dao.UserDao" />  

<bean id="userService" class="com.nfjh.spring6.service.UserService">
<property name="userdao" ref="userDaoBean"/>
</bean>
  • 启动Spring容器,解析spring.xml文件,并且实例化所有的bean对象,放到spring容器当中。
  • 根据bean的id从Spring容器中获取这个对象。
<!--
name 是setXxx()方法的xxx ㊟:value 为简单类型赋值
ref 为引用类型赋值,且要求当前bean工厂中存在该引用类型对象
ref中填写的是bean的id
-->
<bean id="" class="">
<property name="" value=""/>
<property name="" ref=""/>
</bean>
public class UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert(){
logger.debug("保存信息到数据库!\n");
}
} public class UserService {
private UserDao userdao;
public void setUserdao(UserDao userdao) {
this.userdao = userdao;
}
public void saveUser(){
userdao.insert();
} @Test
public void testInsert(){
ApplicationContext applicationContext = (ApplicationContext) new ClassPathXmlApplicationContext("spring-core.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.saveUser();
} <bean id="userDao" class="com.nfjh.spring6.dao.UserDao" /> <bean id="userService" class="com.nfjh.spring6.service.UserService">
<property name="userdao" ref="userDao"/>
</bean>

构造注入

  • javaBean中存在构造方法

<bean id="userDaoBean" class="com.nfjh.spring6.dao.UserDao" /> <bean id="customerServiceBean" class="com.nfjh.spring6.service.CustomerService">
<constructor-arg index="0" ref="userDaoBean"/>
</bean>
  • applicationContext.xml文件中进行了配置
  • 启动Spring容器,解析spring.xml文件,并且实例化所有的bean对象,放到spring容器当中。
  • 根据bean的id从Spring容器中获取这个对象。

set注入的方式

使用set方法进行注入,一定得有无参构造,不要因为写了有参数的构造方法,
让无参数的方法被覆盖了,这时需要手动添加无参构造
注入方式 类类型 操作记忆 bean[id & class] > 操作对象
外部Bean 简单 property[name & value] 简单类型 attrName;
引用 property[name & ref] bean[id & class]
内部Bean --- property[name] > bean[class] bean[id & class] OR 简单类型 attrName;
级联属性 引用 property[name="注入类的属性名" & ref] + property[name="注入类的属性名.属性名" & value] bean[id & class]
数组 简单 property[name] > array > value 简单类型[] attrName;
引用 property[name] > array > ref[bean] 引用类型[] attrName;
List/Set 简单 property[name] > list/set > value List/Set<简单类型> attrName;
引用 property[name] > list/set > ref[bean] List/Set <引用类型> attrName;
Map Map property[name] > map > entry[key/key-ref &value/value-ref] Map<类型1,类型2> attrName;
Perperties Perperties property[name] > props > prop[key] Map<String,String>
null --- property[name] > null单标签 bean[id & class] OR 简单类型 attrName;
--- 对哪个属性注入null值,就啥都不写 bean[id & class] OR 简单类型 attrName;
空字符串 String property[name & value=""] String
String property[name] > value单标签 String
特殊符号 property[name & value="实体符号"]
property[name] > value{ <![CDATA[值]]>}
注入方式 类类型 操作记忆 操作对象
p 命名空间 简单 bean[id & class & p:属性名="属性值" ] 简单类型 attrName;
引用 bean[id & class & p:属性名-ref="bean的id"] bean[id & class]
xmlns:p="http://www.springframework.org/schema/p"
c 命名空间 简单_属性名 bean[id & class c:属性名="属性值"] 简单类型 attrName;
简单_下标 bean[id & class c:_0="属性值"] 简单类型 attrName;
引用_属性名 bean[id & class c:属性名-ref="bean的id"] bean[id & class] OR 简单类型 attrName;
引用_下标 bean[id & class c:_0-ref="bean的id"] bean[id & class] OR 简单类型 attrName;
xmlns:c="http://www.springframework.org/schema/c"
util命名空间 List bean[id & class] > property[name & ref="util的id"] util:list[id] > value
util命名空间 Set -- 同List -- util:set[id] > value
util命名空间 Properties -- 同List -- util:properties[id] > prop[key]

外部Bean

就是使用ref属性来引入。这就是注入外部Bean

    <!--声明/定义Bean-->
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"></bean> <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<!--使用ref属性来引入。这就是注入外部Bean-->
<property name="orderDao" ref="orderDaoBean"/>
</bean>

内部bean

<!--内部bean-->
<!--Ctrl + Alt + Shift + C-->
<bean id="osBeanIn" class="com.nfjh.spring6.service.OrderService">
<property name="orderDao">
<bean class="com.nfjh.spring6.dao.OrderDao"/>
</property>
</bean> 内部bean在定义的时候放在 property标签中
property标签中不加 ref
bean标签中不加 id

简单类型注入

<!--注入简单类型-->
<bean id="userBean" class="com.nfjh.spring6.bean.User">
<property name="username" value="zhangsan"/>
<property name="passwd" value="123"/>
<property name="age" value="20"/>
</bean>

哪些是简单类型

双击shift → BeanUtils → Ctrl + F12

isSimpleType()

对于Date是简单类型,但是需要严格按照格式写日期,不急麻烦

在平常使用时直接当成引用类型,使用ref进行注入

  • 基本数据类型
  • 基本数据类型对应的包装类
  • String或其他的CharSequence子类
  • Number子类
  • Date子类
  • Enum子类
  • URI
  • URL
  • Temporal子类
  • Locale
  • Class
  • 另外还包括以上简单值类型对应的数组类型。

简单类型可以用于对数据源信息的注入

级联属性

User
private String username;
private String passwd;
private int age;
以及对应的get set toString方法 Client
private User user;
private String clientName;
以及对应的get set toString方法
<!--级联属性赋值-->
<bean id="userBeanCascade" class="com.nfjh.spring6.bean.User"/> <bean id="clientBean" class="com.nfjh.spring6.bean.Client">
<property name="clientName" value="QQ"/>
<!--使用级联属性赋值,Client 中要有getUser()方法-->
<property name="user" ref="userBeanCascade"/>
<!--使用级联属性赋值,user必须要有对应的username、passwd、age get方法-->
<property name="user.username" value="张三"/>
<property name="user.passwd" value="123456"/>
<property name="user.age" value="21"/>
</bean>

注入数组

Lseeon
private String [] lessonName ;
以及对应的set toString方法 Student
private String name;
private Lesson [] lessons;
以及对应的set toString方法
<!--
简答类型数组注入
property > array > value private String [] lessonName ;-->
<bean id="lessonBean" class="com.nfjh.spring6.bean.Lesson">
<property name="lessonName">
<array>
<value>JAVA</value>
<value>MYBATIS</value>
<value>SPRING</value>
</array>
</property>
</bean> <bean id="lessonBean2" class="com.nfjh.spring6.bean.Lesson">
<property name="lessonName">
<array>
<value>MYSQL</value>
<value>JDBC</value>
<value>AJAX</value>
</array>
</property>
</bean> <!--
......
Lesson lessonBean = applicationContext.getBean("lessonBean", Lesson.class);
System.out.println(lessonBean);
//Lesson{lessonName=[JAVA, MYBATIS, SPRING]}
--> <!--引用类型数组注入
private String name;
private Lesson [] lessons; property > array > ref[bean]
-->
<bean id="studentBean" class="com.nfjh.spring6.bean.Student">
<property name="name" value="张三"/>
<property name="lessons">
<array>
<ref bean="lessonBean"/>
<ref bean="lessonBean2"/>
</array>
</property>
</bean> <!--
......
Student studentBean = applicationContext.getBean("studentBean", Student.class);
System.out.println(studentBean);
//Student{name='张三', lessons=[Lesson{lessonName=[JAVA, MYBATIS, SPRING]}, Lesson{lessonName=[MYSQL, JDBC, AJAX]}]} -->

List 和Set集合注入

Person
private List<User> userList;
private Set<User> userSet;
以及对应的set toString方法
<!--
㊟:List中的元素可以重复
Set中的元素如果重复写,不报错,但是相同元素只算1个
--> <!--List注入
简单类型
property > list > value
引用类型
property > list > ref[bean]
--> <!-- set注入
简单类型
property > list > value
引用类型
property > list > ref[bean]
--> <bean id="personBean" class="com.nfjh.spring6.bean.Person">
<property name="userList">
<list>
<ref bean="userBean"/>
<ref bean="userBeanCascade"/>
</list>
</property>
</bean> <!--
//Person{userList=[User{username='zhangsan', passwd='123', age=20}, User{username='张三', passwd='123456', age=21}], userSet=null}
//通过此程序可以得知
//通过级联属性赋值的bean可以通过 id 实例化,拿到其中的值
--> <bean id="personBean2" class="com.nfjh.spring6.bean.Person">
<property name="userList">
<list>
<ref bean="userBean"/>
<ref bean="userBeanCascade"/>
</list>
</property>
<property name="userSet">
<set >
<ref bean="userBean"/>
<ref bean="userBeanCascade"/>
</set>
</property>
</bean>
<!--
Person{userList=[User{username='zhangsan', passwd='123', age=20}, User{username='张三', passwd='123456', age=21}], userSet=[User{username='zhangsan', passwd='123', age=20}, User{username='张三', passwd='123456', age=21}]} -->

Map 注入

还是person
再加上一个属性
private Map<Integer,User> userMap;
以及对应的set toString方法
<!--Map注入
简单类型
property > map > entry[key & value]
引用类型
property > map > enter[key-ref & value-ref] -->
<bean id="personMapBean" class="com.nfjh.spring6.bean.Person">
<property name="userMap">
<map>
<entry key="1" value-ref="userBean"/>
<entry key="2" value-ref="userBeanCascade"/>
</map>
</property>
</bean>
<!--
......
Person personMapBean = applicationContext.getBean("personMapBean", Person.class);
System.out.println(personMapBean);
//Person{userList=null, userSet=null, userMap={1=User{username='zhangsan', passwd='123', age=20}, 2=User{username='张三', passwd='123456', age=21}}}
-->

Perperties类型注入

/*虽然properties也是Map但是他的注入方式与Map不同*/
还是person
再加上一个属性
Properties properties ;
以及对应的set toString方法
    <!--特殊Map类型Properties类型注入
property > props > prop[key](内容)
--> <bean id="personProBean" class="com.nfjh.spring6.bean.Person">
<property name="properties" >
<props>
<!--注意 prop的key 与值都是String类型-->
<prop key="driver">com.nfjh.hello</prop>
<prop key="url">www.baidu.com</prop>
</props>
</property>
</bean> <!--
......
Person personProBean = applicationContext.getBean("personProBean", Person.class);
System.out.println(personProBean);
//Person{userList=null, userSet=null, userMap=null, properties={driver=com.nfjh.hello, url=www.baidu.com}}
-->

注入null

User
private String username;
private String passwd;
private int age;
以及对应的get set toString方法
<!--null值的注入-->
<bean id="userNullBean" class="com.nfjh.spring6.bean.User">
<!--第一种注入方式,不注入自动注入null-->
<!--此处不给age 注入值-->
<!--第二中注入方式,手动注入null-->
<property name="username">
<null/>
</property> <!--错误的注入,这种是注入了"null"字符串,不是注入正真的null-->
<property name="passwd" value="null"/>
</bean> <!--
......
User userNullBean = applicationContext.getBean("userNullBean", User.class);
System.out.println(userNullBean);
System.out.println(userNullBean.getPasswd().toUpperCase());
/*
User{username='null', passwd='null', age=0}
NULL 对于第三种错的注入的解释:
如果 真的注入的是null的话,此处应该报空指针异常
但运行结果确实"null"字符串被转化成了大写的字母
所以未注入成功null 另外对于age属性,我的却没有注入值
但结果却是0 ,这点需要注意
*/
-->

注入空字符串

还是User
<!--
空字符串的注入
-->
<!--null值的注入-->
<bean id="userStrNullBean" class="com.nfjh.spring6.bean.User">
<!--第一种注入方式 value中不给值即可-->
<property name="username" value=""/> <!--第二中注入方式,手动标签注入""-->
<property name="passwd">
<value/>
</property>
</bean> <!--
......
User userStrNullBean = applicationContext.getBean("userStrNullBean", User.class);
System.out.println(userStrNullBean);
//User{username='', passwd='', age=0}
-->

< " ' & > 5个特殊符号的注入

    <!--特殊符号的注入-->
<bean id="specialSymbolsBean" class="com.nfjh.spring6.bean.User">
<!--第一种:只用实体符号
这里就之列出一种
> &gt;
<property name="username" value="2 < 3"/>
-->
<property name="username" value="2 &gt; 3"/> <!--第二种:使用value标签 + <![CDATA[2<3]]>-->
<property name="passwd">
<value> <![CDATA[2<3]]> </value>
</property>
<!--
注意点1:
要使用value标签直接放在value中是不对的!!!!
<property name="passwd" value="<![CDATA[2<3]]>"/> 注意点2:
<value></value>中的内容为注入的内容
所以如果这么写结果为
<property name="passwd">
<value>
<![CDATA[2<3]]>
</value>
</property>
User{username='2 > 3', passwd='
2<3
', age=0} 注意点3:
<![CDATA[]]> 中的字符全部要大写
注意点:
不管是使用实体符还是!<[CDATA[]]>这都是XML的语法
--> </bean> <!--
User specialSymbolsBean = applicationContext.getBean("specialSymbolsBean", User.class);
System.out.println(specialSymbolsBean);
//User{username='2 > 3', passwd=' 2<3 ', age=0}
-->

小结

外部bean

<property name="orderDao" ref="orderDaoBean"/>

p命名空间注入

p命名空间注入的底层还是"set"注入
所以仍然需要提供set方法
只不过这种注入方式会让spring配置更加简单

xmlns:p="http://www.springframework.org/schema/p"
User
private String username;
private String passwd;
private int age;
以及对应的get set toString方法 Client
private User user;
private String clientName;
以及对应的get set toString方法

<!--
p命名空间
1、修改头部
添加 xmlns:p="http://www.springframework.org/schema/p" 2、直接使用
简单类型
p:属性名="属性值"
引用类型
p:属性名-ref="bean的id"
--> <bean id="userBean" class="com.nfjh.spring6.bean.User" p:age="0" p:passwd="haha" p:username="zhangsan"/>
<bean id="clientBean" class="com.nfjh.spring6.bean.Client" p:clientName="QQ" p:user-ref="userBean"/> <!--
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("p-namespace.xml");
Client clientBean = applicationContext.getBean("clientBean", Client.class);
System.out.println(clientBean);
//Client{user=User{username='zhangsan', passwd='haha', age=0}, clientName='QQ'}
-->

c 命名空间注入

c命名空间注入的底层还是"构造"注入
所以仍然需要提供构造方法
只不过这种注入方式会让spring配置更加简单

修改就不说了,上面改p的这里改成 c 就行了

xmlns:c="http://www.springframework.org/schema/c"
Shop
private String address;
private String name;
private Date businessHours;
以及对应的三个参数构造方法
    <bean id="date" class="java.util.Date"/>  

<!--
<bean id="shopBean" class="com.nfjh.spring6.bean.Shop" c:_0="天津市" c:_1="小张水果" c:_2-ref="date"/>
-->
<bean id="shopBean" class="com.nfjh.spring6.bean.Shop" c:address="天津市" c:name="小张水果" c:businessHours-ref="date"/> <!--
对于c命名空间
可以使用参数下标对参数赋值,也可以使用属性名对参数赋值
Shop shopBean = applicationContext.getBean("shopBean", Shop.class);
System.out.println(shopBean);
//Shop{address='天津市', name='小张水果', businessHours=Fri Oct 28 06:35:03 CST 2022}
-->

util命名空间

简化集合 Map ,提高代码复用率

第一步:和p一样修改
第二步:在第三行的引号中追加

使用
<util:TYPE id="">
..... </util:TYPE>

Book
private List<String> name;
private Set<String> ISBN;
private Properties type;
以及对应的set toString方法
<util:list id="bookList">
<value>三体</value>
<value>活着</value>
</util:list> <util:set id="bookSet">
<value>1211121</value>
<value>22332232</value>
</util:set> <util:properties id="bookProp">
<prop key="1">科幻</prop>
<prop key="2">文学</prop>
</util:properties> <bean id="bookBean" class="com.nfjh.spring6.bean.Book">
<property name="name" ref="bookList"/>
<property name="ISBN" ref="bookSet"/>
<property name="type" ref="bookProp"/>
</bean> <util:list id="bookList2">
<value>流浪地球</value>
<value>诗经</value>
</util:list> <bean id="bookBean2" class="com.nfjh.spring6.bean.Book">
<property name="name" ref="bookList2"/>
<property name="ISBN" ref="bookSet"/>
<property name="type" ref="bookProp"/>
</bean>

基于XML的自动装配

不管是使用 autowire="byName"
还是autowire="byType"
底层都是使用set方法
User
private String username;
private String passwd;
private int age;
以及对应的get set toString方法 Client
private User user;
private String clientName;
以及对应的get set toString方法

autowire="byName"

    <!--根据名称自动装配
1、添加 autowire属性
2、被装配的bean的id必须是setXxx()方法的xxx
-->
<bean id="clientBean" class="com.nfjh.spring6.bean.Client" autowire="byName">
<property name="clientName" value="QQ"/>
<!--
这里没有了
<property name="user" ref="..."/>
-->
</bean> <!--这里的id不能随便写了-->
<bean id="user" class="com.nfjh.spring6.bean.User">
<property name="age" value="20"/>
<property name="passwd" value="1221"/>
<property name="username" value="zhangsan"/>
</bean>

autowire="byType"

User 中添加该方法
public void testAutoWire(){
System.out.println("user对象已经实例化");
}
    <!--
根据类型进行自动进行装配
1、添加 autowire属性 值为 byType
2、被装配的bean可以没有id,且只能有一个实例,如果有多个就会出现错误
3、如果xml文件中只有一个bean,但是爆红了,不用管,因为Spring扫描的是全部的xml文件
可以降低xml文件的报红的级别,让他不报红,或者直接不管
-->
<!--
<bean class="java.util.Date"/>
Date类型注入失败,为null
怀疑是普通类型,不走byType进行注入
--> <bean id="user" class="com.nfjh.spring6.bean.User">
<property name="age" value="20"/>
<property name="passwd" value="1221"/>
<property name="username" value="zhangsan"/>
</bean> <bean id="clientBean2" class="com.nfjh.spring6.bean.Client" autowire="byType">
<property name="clientName" value="QQ"/>
</bean>
<!-- ApplicationContext applicationContext = new ClassPathXmlApplicationContext("autowire.xml");
Client clientBean = applicationContext.getBean("clientBean2", Client.class);
clientBean.getUser().testAutoWire();;
System.out.println(clientBean);
//user对象已经实例化
//Client{user=User{username='zhangsan', passwd='1221', age=20}, clientName='QQ'}
-->

spring引入外部属性配置文件

步骤

1、准备jdbc.properties文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:2022/spring6
username=root
password=root
initialSize=5
maxIdle=10

2、在xml文件中添加context命名空间以及约束文件

修改与util命名空间的修改时一样的
只不过把该util的地方该成context即可

3、准备好接收数据的几个属性

这里我创建了一个普通的类,用来接收数据

JDBC
private String driver;
private String url;
private String passwd;
private String initialSize;
private String maxIdle;
以及对应的set和toString方法

3、引入配置文件

<context:property-placeholder location="jdbc.properties"/>

<!--
注意这样写jdbc.properties 文件是放在resources类的根路径下的
-->

4、注入数据

<bean id="jdbcBean" class="com.nfjh.spring6.bean.JDBC" >
<property name="driver" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="passwd" value="${password}"/>
<property name="initialSize" value="${initialSize}"/>
<property name="maxIdle" value="${maxIdle}"/>
</bean> <!--
JDBC jdbcBean = applicationContext.getBean("jdbcBean", JDBC.class);
System.out.println(jdbcBean);
//JDBC{driver='com.mysql.jdbc.Driver', url='jdbc:mysql://localhost:2022/spring6', username='34838', passwd='root', initialSize='5', maxIdle='10'}
-->

${username}加载错误

需要注意的是Spring 的配置文件中有一个坑!!!
Spring 在加载配置文件的时候,会先加载系统的环境变量 此时如果jdbc.properties文件中存在username就会被加载为电脑的用户名
而不是我们的配置 解决的方式有三种 1、直接更换名字
把username换成 user_name 或者 name 或者其他的
但注意,spring在加载时不区分大小写,所以改成 userName是没用滴
还有当使用数据源时,存在无法更改配置文件中的名称,否则无法成功创
建连接的情况,此时建议使用其他的解决方案 2、添加配置信息local-override="true"
<context:property-placeholder local-override="true" location="jdbc.properties"/> 3、[已过时,但能用]使用另一个标签,并添加p命名空间 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:localOverride="true">
<property name="locations" value="classpath:jdbc.properties"></property>
</bean>

§5 Bean的作用域

public class ScopeBean {
public ScopeBean() {
System.out.println("无参构造执行了!");
}
} @Test
public void testScopeBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean-scope.xml");
System.out.println("-------------");
ScopeBean scopeBean = applicationContext.getBean("scopeBean", ScopeBean.class);
System.out.println(scopeBean);
ScopeBean scopeBean2 = applicationContext.getBean("scopeBean", ScopeBean.class);
System.out.println(scopeBean2);
ScopeBean scopeBean3 = applicationContext.getBean("scopeBean", ScopeBean.class);
System.out.println(scopeBean3);
    <!--spring默认bean的构建使用单例模式
单例模式对象给的创建在
new ClassPathXmlApplicationContext()的执行中
-->
<bean id="scopeBean" class="com.nfjh.spring6.bean.ScopeBean" scope="singleton"/>
<!--
/* 单例模式下:
无参构造执行了!
-------------
com.nfjh.spring6.bean.ScopeBean@75f95314
com.nfjh.spring6.bean.ScopeBean@75f95314
com.nfjh.spring6.bean.ScopeBean@75f95314
* */
-->
    <!--可以更改为原型模式(每次获取的都是新的实例对象)
此时执行new ClassPathXmlApplicationContext()时不在创建对象
在执行 getBean()方法时创建对象
-->
<bean id="scopeBean" class="com.nfjh.spring6.bean.ScopeBean" scope="prototype"/> <!--㊟:
prototype ['prәutәtaip]
n. 原型
[计] 样机; 原型
--> <!--
/*
-------------
无参构造执行了!
com.nfjh.spring6.bean.ScopeBean@15dcfae7
无参构造执行了!
com.nfjh.spring6.bean.ScopeBean@3da05287
无参构造执行了!
com.nfjh.spring6.bean.ScopeBean@1e636ea3
*/
-->

§6 bean的获取方式

构造方法获取bean

简单工厂获取bean

public class Sweet {
public Sweet(){}
public void getType(){
System.out.println("这是一个水果糖");
}
}
public class SweetFactory {
//注意这个方法是静态的
public static Sweet get(){
return new Sweet();
}
}
<!--
1、简单工厂模式给"工厂类"注入
2、简单工厂模式的get方法是静态方法
使用1个bean 标签即可获取 Spring提供的实例化方式,第二种:通过简单工厂模式。你需要在Spring配置文件中告诉Spring框架,调用哪个类的哪个方法获取Bean
factory-method 属性指定的是工厂类当中的静态方法。也就是告诉Spring框架,调用这个方法可以获取Bean。
--> <bean id="sweetFactory" class="com.nfjh.spring6.factory.SweetFactory" factory-method="get"/>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("factory.xml");
Sweet sweet = applicationContext.getBean("sweetFactory", Sweet.class);
sweet.getType();
//这是一个水果糖

Factory-bean获取bean

Sweet还是使用上面的那个,原封不动

public class SweetFactor_Bean {
//此方法不在是静态的了
public Sweet get(){
return new Sweet();
}
}
<!--
factory-bean的方式获取对象
需要两个bean标签
Spring提供的实例化方式,第三种:通过工厂方法模式。通过 factory-bean属性 + factory-method属性来共同完成
告诉Spring框架,调用哪个对象的哪个方法来获取Bean。
-->
<bean id="sweetFactoryBean" class="com.nfjh.spring6.factory.SweetFactor_Bean"/>
<bean id="sweetBean" factory-bean="sweetFactoryBean" factory-method="get"/> <!--
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("factory.xml");
Sweet sweetBean = applicationContext.getBean("sweetBean", Sweet.class);
sweetBean.getType();
//这是一个水果糖
-->

实现FactoryBean接口重写方法获取

/*
对于实现FactoryBean接口重写getObject()的方式
其实就是对第三中方式的简化
*/ 还是Sweet不动 FactoryBeanImpl
import org.springframework.beans.factory.FactoryBean; //该类实现了FactoryBean 所以不需要在配置factory-bean
public class FactroyBeanImpl implements FactoryBean<Sweet> { //该类重写了getObject ,所以不在需要配置 factory-method
@Override
public Sweet getObject() throws Exception {
return new Sweet();
} @Override
public Class<?> getObjectType() {
return null;
} //返回的对象是否为单例对象,TRUE为单例,FALSE为多例
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
<!--FactoryBeanImpl是一个工厂bean
是实现了FactoryBean接口的特殊bean
而辅助spring实例化其它Bean对象的特殊bean
-->
<bean id="sweetFactoryBeanImpl" class="com.nfjh.spring6.factory.FactroyBeanImpl"/> <!--
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("factory.xml");
Sweet sweetBean = applicationContext.getBean("sweetFactoryBeanImpl", Sweet.class);
sweetBean.getType();
//这是一个水果糖
-->
Con
private Date date;
private String clientLog;
private String clientType;
以及对应的set和toSring方法
import java.text.SimpleDateFormat;
import java.util.Date; public class DateFactoryBeanImpl implements FactoryBean<Date> {
private String strDate; public DateFactoryBeanImpl(String strDate) {
this.strDate = strDate;
} @Override
public Date getObject() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(strDate);
} @Override
public Class<?> getObjectType() {
return null;
}
}
<!--通过factoryBean的方式创建Date对象-->
<bean id="date" class="com.nfjh.spring6.factory.DateFactoryBeanImpl">
<constructor-arg value="2001-09-20"/>
</bean> <bean id="conBean" class="com.nfjh.spring6.factory.Con">
<property name="date" ref="date"/>
<property name="clientType" value="QQ"/>
<property name="clientLog" value="QQ_Log"/>
</bean> <!--
Con conBean = applicationContext.getBean("conBean", Con.class);
System.out.println(conBean);
//Condate=Thu Sep 20 00:00:00 CST 2001, clientLog='QQ_Log', clientType='QQ'}
--> <!-- 当然也可以使用其他方式对Con中的date进行注入
比如内部bean等
-->
<bean id="conBean2" class="com.nfjh.spring6.factory.Con">
<property name="date">
<bean class="com.nfjh.spring6.factory.DateFactoryBeanImpl">
<constructor-arg value="2001-09-20"/>
</bean>
</property>
<property name="clientType" value="WeChat"/>
<property name="clientLog" value="WeChat_Log"/>
</bean>
<!-- Con{date=Thu Sep 20 00:00:00 CST 2001, clientLog='WeChat_Log', clientType='WeChat'} -->

bean的生命周期

Student 

public class Student {
private int age; public Student() {
System.out.println("无参构造执行了!!!");
} public void setAge(int age) {
System.out.println("赋值执行了");
this.age = age;
} public void init(){
System.out.println("init初始化执行了!!!");
} public void sleep(){
System.out.println("bean对象正在使用中!!!");
} public void destroy(){
System.out.println("destroy方法执行了!!!");
}
}
@Test
public void testLifeCycle(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("lifecycle.xml");
Student student = applicationContext.getBean("lifecycleBean", Student.class);
student.sleep(); //销毁 [ApplicationContext没有close这个方法,需要向下转型]
ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
context.close(); //如果不加上这个关闭,destroy方法不会执行
}

5步

<bean id="lifecycleBean" class="com.nfjh.spring6.life_cycle.Student" init-method="init" destroy-method="destroy">
<property name="age" value="21"/>
</bean> <!--
无参构造执行了!!!
赋值执行了
init初始化执行了!!!
bean对象正在使用中!!!
destroy方法执行了!!!
-->

7步

添加一个类实现BeanPostProcessor接口
重写其中的两个方法 import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor; public class LifeCycleAllBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//初始化之前执行
System.out.println("初始化之前执行");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//初始化之后执行
System.out.println("初始化之后执行");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
<!--这个配置针对当前XML中所有的bean有效-->
<bean id="postProcessor" class="com.nfjh.spring6.life_cycle.LifeCycleAllBeanPost"/> <bean id="lifecycleBean" class="com.nfjh.spring6.life_cycle.Student" init-method="init" destroy-method="destroy">
<property name="age" value="21"/>
</bean> <!--对于相同的测试程序 7 步的结果为
无参构造执行了!!!
赋值执行了
初始化之前执行
init初始化执行了!!!
初始化之后执行
bean对象正在使用中!!!
-->

10步

5步

无参构造执行了

赋值执行了

initBean初始化执行了

bean对象正在使用中

destroyBean方法执行了

7步 在init的前后添加bean后处理器(BeanPostProcessor)

的postProcessBeforeInitialization

和postProcessAfterInitialization

10步是在

bean后处理器的Before方法前后添加

Aware的相关接口的检查,设置依赖

和InitializingBean接口的检查,调用接口方法

在使用bean之后添加
DisposableBean接口的检查,调用接口方法

Spring容器只对单例(singleton)的Bean进行完整的生命周期管理。

如果是prototype(多例)作用域的Bean,Spring容器只负责将该Bean初始化完毕。等客户端程序一旦获取到该Bean之后,Spring容器就不再管理该对象的生命周期了。

DefaultListableBeanFactory

@Test
public void registerBean(){
//让自己创建完毕的类纳入Spring容器的管理
Student student = new Student();
student.setAge(33); DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerSingleton("studentBean",student); Object studentBean = factory.getBean("studentBean");
System.out.println(student);
}

§7 IoC 注解式开发

Spring注解使用

- 第一步:加入aop的依赖
- 第二步:在配置文件中添加context命名空间
- 第三步:在配置文件中指定扫描的包
- 第四步:在Bean类上使用注解

声明Bean

@Component  声明所有类型 Bean

以下三个注解是@Component 注解的别名,可以使用@Component代替
但是代码的可读性会下降 @Controller 声明界面层的bean
@Service 声明业务逻辑层的bean
@Repository 声明数据访问层的bean
User.java
@Component(value="userBean")
如果注解的属性名是value,那么value是可以省略的@Component("userBean")
如果把value属性彻底去掉,spring会给Bean自动取名吗?会的。并且默认名字的规律是:Bean类名首字母小写即可@Component ==> getBean("user"); 如果是多个包怎么办?有两种解决方案: 第一种:在配置文件中指定多个包,用逗号隔开
第二种:指定多个包的共同父包

我的疑问与测试

如果一个类中添加了注解,同时又在XML文件中进行了bean的配置

会不会报错,是两个都能用,还是两个都不能用

如果能用获得的对象是同一个吗[我只测试了单例模式下]

@Component
public class User {
}
<context:component-scan base-package="com.nfjh.bean"/>
<bean id="userBean" class="com.nfjh.bean.User"/>
@Test
@Test
public void testBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Annotation-test.xml");
Object userBean = applicationContext.getBean("userBean");
Object userBean2 = applicationContext.getBean("user");
System.out.println(userBean);
System.out.println(userBean2);
}
/*
结果为两个都可以用,且获得的对象不是同一个
com.nfjh.bean.User@7756c3cd
com.nfjh.bean.User@2313052e
*/

指定注解类型生效[过滤]

    <!--

先设置过滤所有  再确定只扫描的类型
context:component-scan[base-package & use-default-filters="false"] > context:include-filter[type expression] 先所有的都扫描,再设置不扫描的类型
context:component-scan[base-package & use-default-filters="true"] > context:exclude-filter [type expression] base-package 需要扫描的包
use-default-filters
false表示全过滤 context:include-filter 需要添加的扫描
true表示全扫描 context:exclude-filter 需要过滤的类型 若为true use-default-filters="true" 可以不写 type annotation
expression
org.springframework.stereotype.Repository
org.springframework.stereotype.Service
org.springframework.stereotype.Controller
org.springframework.stereotype.Component
-->

注入数据

@Value

以前这样注入

<!--注入简单类型-->
<bean id="userBean" class="com.nfjh.spring6.bean.User">
<property name="username" value="zhangsan"/>
<property name="passwd" value="123"/>
<property name="age" value="20"/>
</bean>

使用注解这样注入

1、在属性上注入

@Component("userAnnotation")
public class User {
@Value("张三")
private String username;
@Value("123456")
private String passwd;
@Value("21")
private int age; @Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", passwd='" + passwd + '\'' +
", age=" + age +
'}';
}
}
/* ㊟:使用@Value的注解方式,可以不用写构造方法和set以及get方法
toString方法只是用做输出看比较方便,也可以省略 User{username='张三', passwd='123456', age=21}
*/

2、在set方法上注入

@Component("userAnnotation")
public class User {
private String username;
private String passwd;
private int age; @Value("张三")
public void setUsername(String username) {
this.username = username;
}
@Value("123456")
public void setPasswd(String passwd) {
this.passwd = passwd;
}
@Value("23")
public void setAge(int age) {
this.age = age;
} /*
User{username='张三', passwd='123456', age=23}
*/

3、在构造方法的参数上进行注入

@Component("userAnnotation")
public class User {
private String username;
private String passwd;
private int age; public User(@Value("李四") String username, @Value("3012") String passwd, @Value("23") int age) {
this.username = username;
this.passwd = passwd;
this.age = age;
} /*
User{username='李四', passwd='3012', age=23}
*/

自动装配

@AutoWired 类型自动装配

AutoWired根据类型自动装配时,类型需要唯一

autowireTest
dao
UserDao
@Repository
public interface UserDao {
void insert();
} impl
@Repository
public class UserDaoForJK implements UserDao{
@Override
public void insert() {
System.out.println("计科学生信息插入中");
}
} service
UserService
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void generate(){
userDao.insert();
} }
<!--添加包扫描-->
<context:component-scan base-package="com.nfjh.autowireTest"/>
@Test
public void testAnnotationAutoWiredInjection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Annotation-test.xml");
Object userService = applicationContext.getBean("userService");
UserService service = (UserService) userService;
service.generate();
}
//计科学生信息插入中

缺点

如何解决同一接口有多个实现类,无法通过类型注入? 

如果在impl包下添加实现类
@Repository
public class UserDaoForTX implements UserDao {
@Override
public void insert() {
System.out.println("通信学生信息插入中");
}
}
如果同一个类型有多个实现类,则Spring无法通过AutoWired直接进行装配
例如下面如果UserDao 的实现类除了UserDaoForJK之外还有一个userDaoForTX的话
在运行测试程序时会报错
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.nfjh.autowireTest.dao.UserDao' available: expected single matching bean but found 2: userDaoForJK,userDaoForTX 注意:如果UserDao 有多页实现类,但是只有一个实现类添加了@Component以及他的别名,且
其他实现类也没有在XML文件中进行<bean>的配置
这种只算一个,也是可以正常装配的

@AutoWired + Qualifer名称自动装配

对与以上使用类型无法自动装载的问题,可以使用名称自动装载进行解决

在UserService中这样写

@AutoWired
@Qualifer("userDaoForTX")
private UserDao userDao;
注意@Qualifer括号中的内容是bean的id 即可指定UserService中装载的对象为userDaoForTX

@AutoWired出现的位置

以下举例以使用@AutoWired类型装配有多个实现类无法解决

为例,但是注意,当可以单独使用@AutoWired解决时,@AutoWired也是可以出现在这些位置上的

属性上,set方法上,构造方法上,构造方法的参数上,省略

1、属性上上面已经出现了,不再举例

2、set方法上

@Autowired
@Qualifier("userDaoForJK")
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

3、构造方法上

    @Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
} 需要注意的是,@Qualifier限定符不适用于构造函数
" @Qualifier is not applicable for constructor"
所以在测试时,我把userDaoForJK类上的@Repository 给注释了
但是虽然@Qualifier无法在构造方法上使用,但是@Autowired可以

4、构造方法的参数上

public UserService(@Autowired  @Qualifier("userDaoForJK") UserDao userDao) {
this.userDao = userDao;
}

5、不使用@Autowired

当且仅当有参数的构造方法只有一个,@Autowired注解可以省略

当然如果有多个构造方法,@Autowired不能省略,即使是无参的构造和有参构造,也不能省

public UserService(UserDao userDao) {
this.userDao = userDao;
} [OS]最好不要省略,会降低代码的可读性!!

@Resource

使用使用这个注解是需要引入jar包的

Spring6支持的javaEE9 所以需要引入的是

<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
dao
public interface StudentDao {
void delete();
} impl
@Repository
public class StudentDaoImpl implements StudentDao {
@Override
public void delete() {
System.out.println("学生信息删除....");
}
} service
@Service
public class StudentService { @Resource(name="studentDaoImpl")
private StudentDao studentDaoProp; public void delStu(){
studentDaoProp.delete();
}

1、@Resource 默认使用名称进行装配

当StudentDaoImpl的注解中value是空的
和上面使用@AutoWired结合@Qualifier一样名称默认是类名的首字母小写
@Resource(name="studentDaoImpl") 当value不为空时填写的
在使用@Resource时name="名称"

2、当名称为空时,属性名做为名称进行查找

impl
@Repository("studentDaoProp")
public class StudentDaoImpl implements StudentDao service
@Resource
private StudentDao studentDaoProp;

3、当名称为空,且根据属性名无法查找到类时,根据类型进行查找

注意此时根据类型查找时,如果类型有多个,和@Autowired一样是不行的

impl
@Repository
public class StudentDaoImpl implements StudentDao service
@Resource
private StudentDao studentDaoProp;

如果条件符合以上可以查询出结果

总结一下

注解类型 属性上 set 构造方法 构造方法的参数上 来源
@Autowired Spring
@Qualifier × Spring
@Resource × × JDK扩展包

同时,如果只是用@Autowired说明使用类型注入,被注入的类型是唯一的

@Autowired结合 @Qualifer 说明使用名称注入

全注解式开发

通过使用以上的注解,目前我们的配置文件中只有一个包扫描
<context:component-scan base-package="com.nfjh.bean"/> 现在编写一个类,用于代替这个配置文件

@Configuration
@ComponentScan({"com.nfjh.Resource.dao","com.nfjh.Resource.service"})
public class Spring6Config {
} 注意这里的注解 @Configuration 以及 @ComponentScan ,
看清楚不是@ComponentScans,不是s结尾!!!
注意此时已经没有了xml文件,所以这里的测试程序需要修改
使用"AnnotationConfigApplicationContext",传入“配置类”的类名,类名随意起,在这里传入即可 AnnotationConfigApplicationContext annotationConfigApplicationContext
= new AnnotationConfigApplicationContext(Spring6Config.class); StudentService studentService = annotationConfigApplicationContext.getBean("studentService", StudentService.class);
studentService.delStu();

§8 jdbcTemplate

这部分看老杜的笔记

比较简单

AOP 面向切面编程

AOP七大术语

关于面向切面编程我的理解:

将与核心业务逻辑无关的代码进行抽离,形成以横向交叉应用于多个项目的"万金油"代码

例如事务管理,日志,安全等,提高代码的复用率

AOP面向切面编程是一种编程思想,而JDK动态代理和GBLIB动态代理就是AOP的实现

连接点[Joinpoint]

可以织入切面的位置

切点[Pointcut]

核心业务逻辑方法,真正织入切面的的方法

一个切点对应多个连接点

通知[Advice]

我们将要织入的增强代码

[前置通知、后置通知、环绕通知、异常通知、最终通知]

切面[Aspect]

切点 + 通知(在什么位置放什么代码)

织入 [Weaving]

把通知应用到目标对象上的过程。

代理对象[Proxy]

一个目标对象被织入通知后产生的新对象。

目标对象[Target]

被织入通知的对象。

切点表达式

execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])

AspectJ

1、使用Spring+AspectJ的AOP需要引入的依赖如下:

<!--spring context依赖-->
<!--spring aop依赖-->
<!--spring aspects依赖-->

2、Spring配置文件中添加context和aop的命名空间和约束文件

AspectJ框架__注解

5个通知类型

    <!--组件扫描-->
<context:component-scan base-package="com.ndjh.spring6.service"/> <!--交给spring管理,可用注解代替-->
<bean id="logAspect" class="com.ndjh.spring6.service.LogAspect"/>
<bean id="userService" class="com.ndjh.spring6.service.UserService"/> <!-- 开启自动代理-->
<!-- proxy-target-class="true" 强制使用CGLIB动态代理
proxy-target-class="false" 默认值 ,依情况而定使用哪种代理
有接口使用JDK代理,其他使用CGLIB代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
service

UserService
//Target目标对象,待增强
public class UserService {
public void login(){
System.out.println("系统正在进行身份验证");
}
} LogAspect //@Aspect注解给spring检查
@Aspect
public class LogAspect {
//通知+切点
/*
通知就是增强代码
*/ //括号里是切点表达式
//execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
//注意在全限定类名与方法名之间没有空格 //Before注解标注的是前置通知
@Before("execution(* com.ndjh.spring6.service..*(..))")
public void testBefore(){
System.out.println("前置通知");
} @AfterReturning("execution(* com.ndjh.spring6.service..*(..))")
public void testAfterReturning(){
System.out.println("后置通知");
} //注意环绕通知方法有参数,且有调用方法
@Around("execution(* com.ndjh.spring6.service..*(..))")
public void testAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前环绕通知");
joinPoint.proceed();
System.out.println("后环绕通知");
} //若异常环绕通知执行,则后环绕通知不会执行,而最终环绕通知执行
@AfterThrowing("execution(* com.ndjh.spring6.service..*(..))")
public void testAfterThrowing(){
System.out.println("异常通知");
} @After("execution(* com.ndjh.spring6.service..*(..))")
public void testAfter(){
System.out.println("最终通知");
} } /*----在IDEDA2021.3中运行结果------
前环绕通知
前置通知
系统正在进行身份验证
后置通知
最终通知
后环绕通知
-------------------------------*/ 现在修改代码UserService
增加异常
//Target目标对象,待增强
public class UserService {
public void login(){
System.out.println("系统正在进行身份验证");
throw new RuntimeException();
}
} /*----在IDEDA2021.3中运行结果------
前环绕通知
前置通知
系统正在进行身份验证
异常通知
最终通知 java.lang.RuntimeException
at com.ndjh.spring6.service.UserService.login(UserService.java:8)
at com.ndjh.spring6.service.UserService$$FastClassBySpringCGLIB$$93098a3f.invoke(<generated>)
.........异常信息............. 观察可知,最终通知会执行,但是后环绕通知不在执行
-------------------------------*/

切面的顺序

对于“切面类”可以使用注解@Order进行排序

@Order(整型数字)

数字越小越先执行

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
int value() default 2147483647;
}

通用切点表达式

@Pointcut("execution(* com.ndjh.spring6.service..*(..))")
public void generalExpression(){} 使用@Pointcut进行标注 当前类进行标注
@Before("generalExpression()")
public void testBefore(){
System.out.println("前置通知");
} //跨类使用要写全限定类名+方法名()
@Before("com.ndjh.spring6.service.LogAspect.generalExpression()")
public void safetyBefore(){
System.out.println("安全前置通知");
}

AspectJ框架__XML

Spring事务

spring事务失效的12种场景:

访问权限问题

方法用final修饰

方法内部调用

末被spring管理

多线程调用

表不支持事务

未开启事务

错误的传播特性

自己吞了异常

手动抛了别的异常

自定义了回滚异常

嵌套事务回滚多了

spring集成Mybatis

pom文件

  • 仓库地址
  • spring-context
  • spring-jdbc
  • mybatis
  • mybatis-spring
  • jdbc
  • druid
  • junit
mapper :  接口
pojo : 普通类 service
接口
接口的实现类
归入Spring管理加上注解 @Service @Transactional
添加Mapper中的属性,加上注解 @Autowired,需要时再加上 @Qualifer resources:
mapper
Mapper.xml 编写SQL语句
jdbc.properties
mybatis-config.xml
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
spring-config.xml
1、组件扫描
2、引入JDBC配置文件
3、数据源
4、sqlSessionFactoryBean配置
mybatis核心配置文件路径
数据源
指定别名 [sql中的resultType]
5、Mapper扫描mapper包
6、事务管理器
7、开启事务
<?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:tx="http://www.springframework.org/schema/tx"
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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--1、组件扫描-->
<context:component-scan base-package="com.nfjh.bank"/> <!--2、引入JDBC属性文件-->
<context:property-placeholder location="jdbc.properties"/> <!--3、数据源-->
<!--此处对应的Bean不用我们写,阿里巴巴写好了,我们直接用就行-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
</bean> <!--4、SqlSessionFactoryBean配置-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入mybatis核心配置文件路径-->
<property name="configLocation" value="mybatis-config.xml"/>
<!--数据源-->
<property name="dataSource" ref="dataSource"/>
<!--指定别名-->
<property name="typeAliasesPackage" value="com.nfjh.bank"/>
</bean>
<!--5、Mapper扫描mapper包,生成代理类-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.nfjh.bank.mapper"/>
</bean>
<!--6、事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务-->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>

最新文章

  1. 《C#高级编程》读书笔记
  2. poj2531 Network Saboteur
  3. 命令别名alias设置
  4. SQL多条件查询
  5. 微信SDK开发学习
  6. Ehcache(2.9.x) - Configuration Guide, Configuring Storage Tiers
  7. linux查看文件个数命令
  8. iOS 从C移植项目到Objective-C
  9. Swift - 19 - 字典的初始化
  10. mysql触发器使用注意
  11. Windows系统命令行net user命令用法
  12. Linux for周期运行命令注意事项
  13. poj-3898 Software Industry Revolution DP
  14. xamarin android checkbox自定义样式
  15. install_driver(Oracle) failed: Can&#39;t load `.../DBD/Oracle/Oracle.so&#39; for module DBD::Oracle
  16. 《java入门第一季》之StringBuffer小案例
  17. Java基础之抽象类
  18. Spring mvc 下载文件处理
  19. 网络流相关知识点以及题目//POJ1273 POJ 3436 POJ2112 POJ 1149
  20. springboot学习之授权Spring Security

热门文章

  1. CCPC2021 广州 K. Magus Night
  2. CCF 201909-2 小明种苹果(续)
  3. 创建一个httpserver、httpclient
  4. 【BOOK】数据存储--MongoDB
  5. Amd,Cmd, Commonjs, ES6 import/export的异同点
  6. 控制台程序console输入参数 获取参数
  7. mysql高级函数FIND_IN_SET,ENUM和SET,LOCATE,ELT,FIELD,INTERVAL,COUNT,CAST,NULLIF,ISNULL,IFNULL,IF,CONVERT,COALESCE
  8. Yolov3-v5正负样本匹配机制
  9. linux用户权限与组
  10. CAD中相交线怎样打断?CAD打断相交线步骤