Spring框架
Spring 开源 轻量级框架 2003 兴起
目的:解决企业应用的复杂性创建的
核心:控制反转(IOC) 和 面向切面(AOP)
Spring优点:
开源免费框架(容器)
轻量级,非侵入式框架
控制反转(IOC) 和 面向切面(AOP)
良好的事务支持(声明式事务)
高内聚,低耦合
一.IOC容器(控制反转)
Srping 核心,容器将创建对象,把他们连接到一起,配置他们,并管理他们的生命周期(创建到销毁)
Spring容器使用依赖注入(DI)来管理组成一个应用程序的组件,这些对象被称为Spring Beans
控制反转:
含义:依赖容器给予你资源,控制权在容器(Spring)身上,不主动new对象,而是哪里需要对象,变向容器发出请求,让容器帮自己new一个对象
依赖注入
含义:所需求的对象,需要依赖容器注入
核心容器提供Spring框架的基本功能实现,核心容器主要组件BeanFactory,它是工厂模式的实现
BeanFactory使用控制反转(IOC)模式将应用程序配置和依赖于实际应用程序代码分开
二.依赖注入(DI)
1.概念
- 依赖注入(Dependency Injection,DI)。
- 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
- 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配
2.构造器注入
注意,构造器注入是依赖于无参构造实现的!当写入有参构造是,xml会报错
User.java
package com.kuang.pojo;
public class User {
private String name;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--id就是你要new对象的名字
class是你要new对象的类
-->
<!--
id: bean的唯一标识符,也就是相当于我们学过的对象名
class: bean对象所对应的全限定类名,包名+类名
name: 也是别名,name可以同时取多个别名,可以使用逗号 空格 分号来分隔
-->
<bean id="user" class="com.kuang.pojo.User" name="user2 user3 user4">
<property name="name" value="zhangsan"></property>
</bean>
</beans>
3.Set注入(重点)
1.简单案例
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .
案例:
需求说明
输出
张嘎说:“三天不打小鬼子,手都痒痒!”
Rod说:“世界上有10种人,认识二进制的和不认识二进制的。”
User2.java
package com.kuang.pojo;
public class User2 {
private String str;
private String name;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Printer.java
package com.kuang.pojo;
public class Printer {
private User2 user2;
public void print(){
System.out.println(user2.getName()+"说:"+user2.getStr());
}
public void setUser2(User2 user2) {
this.user2 = user2;
}
}
applicationContext.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="beans.xml"></import>
<import resource="beans2.xml"></import>
<import resource="beans3.xml"></import>
<bean id="useA" class="com.kuang.pojo.User2">
<property name="name" value="张嘎"></property>
<property name="str" value="三天不打小鬼子,浑身痒痒"></property>
</bean>
<bean id="useB" class="com.kuang.pojo.User2">
<property name="name" value="烬jing"></property>
<property name="str" value="我于杀戮之间绽放,亦如黎明间的花朵"></property>
</bean>
<!--Printer类要使用,也创建一个bean-->
<bean id="printer" class="com.kuang.pojo.Printer">
<property name="user2" ref="useB"></property>
</bean>
</beans>
测试:
@Test
public void test2(){
//读取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Printer printer = (Printer) context.getBean("printer");
printer.print();
System.out.println(printer);
}
结果:
烬jing说:我于杀戮之间绽放,亦如黎明间的花朵
com.kuang.pojo.Printer@51c8530f
这样,每次更改applicationContext.xml中的ref即可输出不同的语句
案例二:
Spring框架打印机实例
定义一个纸张的接口
package paper;
public interface Paper {
String getSize();
}
定义实现类
package paper.impl;
import paper.Paper;
public class A4Paper implements Paper {
public String getSize() {
return "A4";
}
}
package paper.impl;
import paper.Paper;
public class B5Paper implements Paper {
public String getSize() {
return "B5";
}
}
同理,定义一个墨盒颜色的接口
package color;
public interface Color {
String getColor();
}
定义实现类:
package color.impl;
import color.Color;
public class ColorfulColor implements Color {
public String getColor() {
return "彩色";
}
}
package color.impl;
import color.Color;
public class GrayColor implements Color {
public String getColor() {
return "灰色墨盒";
}
}
打印机类
package printer;
import color.Color;
import paper.Paper;
//打印机类
public class PrinterShow {
private Color color;//这是接口
private Paper paper;//这也是接口
public void printer(){
System.out.println("打印机打印"+color.getColor()+"的颜色,和"+paper.getSize()+"大小的纸张");
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public Paper getPaper() {
return paper;
}
public void setPaper(Paper paper) {
this.paper = paper;
}
}
配置文件
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--有一个类,就定义一个bean-->
<!--这里放的是纸张和墨盒的实现类而不是接口 -->
<!--墨盒-->
<bean id="colorful" class="color.impl.ColorfulColor"></bean>
<bean id="gray" class="color.impl.GrayColor"></bean>
<!--纸张-->
<bean id="a4" class="paper.impl.A4Paper"></bean>
<bean id="a5" class="paper.impl.B5Paper"></bean>
<!--在打印机里引入纸张和墨盒-->
<!--彩色 A4-->
<bean id="printer1" class="printer.PrinterShow">
<property name="color" ref="colorful"></property>
<property name="paper" ref="a4"></property>
</bean>
<!--彩色 A5-->
<bean id="printer2" class="printer.PrinterShow">
<property name="color" ref="colorful"></property>
<property name="paper" ref="a5"></property>
</bean>
<!--灰色 a5-->
<bean id="printer3" class="printer.PrinterShow">
<property name="color" ref="gray"></property>
<property name="paper" ref="a4"></property>
</bean>
<!--灰色 a4-->
<bean id="printer4" class="printer.PrinterShow">
<property name="color" ref="gray"></property>
<property name="paper" ref="a5"></property>
</bean>
<bean id="aha" class="color.impl.GrayColor" ></bean>
</beans>
测试:
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
PrinterShow ps = (PrinterShow) context.getBean("printer4");
ps.printer();
}
结果:
打印机打印灰色墨盒的颜色,和B5大小的纸张
2.set注入实例
scop : 设置bean的作用域
singleton:默认值 在spirng IOC 容器仅存在一个bean ,单例模式
prototype: 每次从容器中调用Bean时 ,都返回一个新的实例 例如 new xxxBean();
request: 每次HTTP请求 都会创建一个新的Bean,作用域仅限于WEBApplicationContext环境
session:同一个HTTP Session共享一个Bean,不同的Session使用不同的Bean,作用域仅限于WEBApplicationContext环境
global-session:一般用于Portlet应用环境,作用域仅限于WEBApplicationContext环境
3.注入设置
编写一个实体类
TestEntity.java
package com.kuang.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* 用于测试IOC 注入 不同的类型数据
*/
public class TestEntity {
private String specialCharacter1; // 特殊字符值1
private String specialCharacter2; // 特殊字符值2
private User innerBean; // JavaBean类型
private List<String> list; // List类型
private String[] array; // 数组类型
private Set<String> set; // Set类型
private Map<String, String> map; // Map类型
private Properties props; // Properties类型
private String emptyValue; // 注入空字符串值
private String nullValue = "init value"; // 注入null值
public void setSpecialCharacter1(String specialCharacter1) {
this.specialCharacter1 = specialCharacter1;
}
public void setSpecialCharacter2(String specialCharacter2) {
this.specialCharacter2 = specialCharacter2;
}
public void setInnerBean(User user) {
this.innerBean = user;
}
public void setList(List<String> list) {
this.list = list;
}
public void setArray(String[] array) {
this.array = array;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProps(Properties props) {
this.props = props;
}
public void setEmptyValue(String emptyValue) {
this.emptyValue = emptyValue;
}
public void setNullValue(String nullValue) {
this.nullValue = nullValue;
}
public void showValue() {
System.out.println("特殊字符1:" + this.specialCharacter1);
System.out.println("特殊字符2:" + this.specialCharacter2);
System.out.println("内部Bean:" + this.innerBean.getName());
System.out.println("List属性:" + this.list);
System.out.println("数组属性[0]:" + this.array[0]);
System.out.println("Set属性:" + this.set);
System.out.println("Map属性:" + this.map);
System.out.println("Properties属性:" + this.props);
System.out.println("注入空字符串:[" + this.emptyValue + "]");
System.out.println("注入null值:" + this.nullValue);
}
}
User.java
package com.kuang.pojo;
public class User {
private String name;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
1、常量注入
基本数据类型和String类型
<!--
id: bean的唯一标识符,也就是相当于我们学过的对象名
class: bean对象所对应的全限定类名,包名+类名
name: 也是别名,name可以同时取多个别名,可以使用逗号 空格 分号来分隔
-->
<bean id="user" class="com.kuang.pojo.User" name="user2 user3 user4">
<property name="name" value="zhangsan"></property>
</bean>
2、**注入包含特殊字符的 字符串 **
<bean id="testEnity" class="com.kuang.pojo.TestEntity">
<!-- 注入包含特殊字符的 字符串 -->
<property name="specialCharacter1">
<!--固定格式: <![CDATA[ 包含有特殊字符的字符串 ]]> -->
<value><![CDATA[&&]]></value>
</property>
<property name="specialCharacter2">
<value><![CDATA[P&G]]></value>
</property>
</bean>
3、**定义内部Bean **
<!--定义内部bean-->
<bean id="testEnity2" class="org.westos.entity.TestEntity">
<property name="innerBean">
<bean class="org.westos.entity.User">
<property name="id" value="1"></property>
<property name="name" value="qq"></property>
<property name="password" value="123456"></property>
</bean>
</property>
</bean>
4、List
<bean id="testEnity3" class="org.westos.entity.TestEntity">
<property name="list">
<list>
<!-- 定义List中的元素 -->
<value>list_篮球</value>
<value>list_足球</value>
</list>
</property>
</bean>
5、数组
<bean id="testEnity4" class="org.westos.entity.TestEntity">
<property name="array">
<array>
<!-- 定义数组中的元素 -->
<value>array_篮球</value>
<value>array_足球</value>
</array>
</property>
</bean>
6、Set
<bean id="testEnity5" class="org.westos.entity.TestEntity">
<property name="set">
<value>set_足球</value>
<value>set_篮球</value>
</property>
</bean>
7、Map
<!-- 注入Map类型 -->
<bean id="testEnity6" class="org.westos.entity.TestEntity">
<property name="map">
<map>
<!-- 定义Map中的键值对 -->
<entry>
<key>
<value>football</value>
</key>
<value>足球</value>
</entry>
<entry>
<key>
<value>basketball</value>
</key>
<value>篮球</value>
</entry>
</map>
</property>
</bean>
8、注入Properties类型
<!-- 注入Properties类型 -->
<bean id="testEnity7" class="org.westos.entity.TestEntity">
<property name="props">
<!-- 定义Properties中的键值对 -->
<props>
<prop key="football">足球</prop>
<prop key="basketball">篮球</prop>
</props>
</property>
</bean>
9、注入空字符串值
<!-- 注入空字符串值-->
<bean id="testEnity8" class="org.westos.entity.TestEntity">
<property name="emptyValue">
<value></value>
</property>
</bean>
10、注入null值
<!--注入null值-->
<bean id="testEnity9" class="org.westos.entity.TestEntity">
<property name="emptyValue">
<null/>
</property>
</bean>
4.构造函数的注入
1.下标注入
<!--构造函数注入,下标注入-->
<bean id="gouzhao" class="org.westos.entity.User">
<constructor-arg index="0" value="1"></constructor-arg>
<constructor-arg index="1" value="lisi"></constructor-arg>
<constructor-arg index="2" value="123456"></constructor-arg>
</bean>
2.名称注入
<!--构造函数注入 名称注入-->
<bean id="www" class="org.westos.entity.User">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="王五"></constructor-arg>
<constructor-arg name="password" value="123456"></constructor-arg>
</bean>
3.类型注入
<!--构造函数注入,类型注入-->
<bean id="qqq" class="org.westos.entity.User">
<constructor-arg type="int" value="1"></constructor-arg>
<constructor-arg type="java.lang.String" value="芜湖大司马"></constructor-arg>
<constructor-arg type="java.lang.String" value="123456"></constructor-arg>
</bean>
5.p命名空间注入
特点:
p 命名空间的特点:使用属性而不是子元素的形式配置Bean的属性,从而简化了配置代码
对于直接量(基本数据类型、字符串)属性:p:属性名=“属性值”
对于引用Bean的属性:p:属性名-ref=“Bean的id”
注意事项:
使用前要先要在Spring配置文件中引入p命名空间
xmlns:p="http://www.springframework.org/schema/p"
<!--p注入-->
<!--p 命名空间的特点:使用属性而不是子元素的形式配置Bean的属性,从而简化了配置代码-->
<bean id="ppp" class="org.westos.entity.User" p:id="1" p:name="zhangsan" p:password="123456">
</bean>
<bean id="userService" class="com.xk.demo.service.impl.UserServiceImpl" p:userDao-ref="userDao">
</bean>
6.设置注入与构造注入的对比
设值注入 | 构造注入 |
---|---|
通过setter访问器实现 | 通过构造方法实现 |
灵活性好,但setter方法数量较多 | 灵活性差,仅靠重载限制太多 |
时效性差 | 时效性好 |
通过无参构造实例化 | 通过匹配的构造方法实例化,但建议保留无参构造 |
二.AOP 面向切面
1.概述
面向对象的延续,spring框架的重要内容,是函数式编程的一种衍生泛型
利用AOP可以将业务逻辑各个部分之间的耦合度降低,提高程序的重用性,提高开发效率
AOP的目标:让我们可以专心做事
1.目标类: target 需要被代理的类(Service层)
2.连接点: Joinpoint 指哪些方法可能被增强
3.切入点: PointCut 已经被增强的连接点
4.通知: advice 增强的代码(方法)
5.织入: Weaving是指把增强advice应用的目标对象target来
创建新的代理对象proxy的过程
6.代理类: proxy代理类
7.切面: Aspect 是切入点PointCut和通知advice的结合
面向切面编程:从系统中分离出切面,独立于业务逻辑(service)实现,在程序“执行时”织入程序中运行(在什么位置,执行什么功能)
配置AOP主要使用aop命名空间下的元素完成,可以实现定义切入点和织入增强等操作
2.步骤:
基本项目位置
实现方式:
1、导入命名空间
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
</beans>
2、将增强类 转换为Bean
增强类:
package com.xk.demo.aop;
import org.apache.log4j.Logger;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
public class UserServiceLogger {
private static Logger log = Logger.getLogger(UserServiceLogger.class);
/**
* 前置增强
* JoinPoint : 目标方法的类名 方法名 参数列表....
*
* @param jp
*/
public void before(JoinPoint jp) {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().
getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
}
/**
* 后置增强
* JoinPoint : 目标方法的类名 方法名 参数列表....
* result :获取目标 方法的返回值
*
* @param jp
* @param result
*/
public void afterReturning(JoinPoint jp, Object result) {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().
getName() + " 方法。方法返回值:" + result);
}
}
增强类
<!--增强类-->
<bean id="userServiceLogger" class="com.xk.demo.aop.UserServiceLogger"></bean>
3.beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<!--增强类-->
<bean id="userServiceLogger" class="com.xk.demo.aop.UserServiceLogger"></bean>
<!--
切入点 规则
public * addNewUser(entity.User): “*”表示匹配所有类型的返回值。
public void *(entity.User): “*”表示匹配所有方法名。
public void addNewUser(..): “..”表示匹配所有参数个数和类型。
* com.service.*.*(..):匹配com.service包下所有类的所有方法。
* com.service..*.*(..):匹配com.service包及其子包下所有类的所有方法
-->
<!--切面-->
<aop:config>
<!--
切入点:PointCut
expression="execution(访问修饰符 返回值类型 方法名( 包名.类名( 参数 ) ))"
.. 任何参数
com.xk.demo.service.*.* 第一个* 表示所有类 第二个*所有方法
-->
<aop:pointcut id="pointcut" expression="execution(* com.xk.demo.service.*.*(..) )"/>
<!-- 可以有很多个增强类 使用哪一个增强类 增强取决于 aop:aspect ref ="beanID"-->
<aop:aspect ref="userServiceLogger">
<!-- aop:before: 固定格式 前置增强 -->
<!-- method 方法名称 (来自增强类) -->
<!-- pointcut-ref 切入点名称 aop:pointcut id="pointcut" -->
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!-- aop:after-returning: 固定格式 后置增强 -->
<!-- method 方法名称 (来自增强类) -->
<!-- pointcut-ref 切入点名称 aop:pointcut id="pointcut" -->
<!--returning 有返回值参数 (来自增强类) -->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
</aop:aspect>
</aop:config>
</beans>