Spring框架
Spring框架是一个集众多设计模式于一身的开源的、轻量级的项目管理框架。致力于JAVAEE轻量级解决方案。
轻量级解决方案:提供一个以简单的、统一的、搞笑的方式构造整个应用,并且可以将单层框架以最佳的组合糅合在一起建立一个连贯的体系
Spring框架的核心作用
Spring框架用来管理[创建|使用|销毁]项目中的组件,由于Spring框架可以帮我们生产项目中组件对象,因此也习惯称Spring是一个工厂|容器
组件:项目中的service,dao,controller都是项目中的组件
Spring框架通常不管理对实体类对象创建
环境搭建
引入依赖
引入配置文件
创建组件
工厂管理
<?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标签负责组件对象的管理 一个bean标签只负责一个组件对象
class:要管理的组件 java类的全限定名 包名.类名
id:组件对象在工厂中的唯一标识 建议命名 存在接口,接口首字母小写 类名首字母小写
-->
<bean id="userDao" class="init.UserDaoImpl"></bean>
</beans>
启动工厂测试
public static void main(String[] args) {
//启动工厂
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
//获取对象
UserDAO userDAO = (UserDAO) context.getBean("userDAO");
userDAO.save("小黑");
}
Spring框架中的核心思想
IOC[控制反转]
IOC (inversion of controll) 控制反转
将对象的创建由原来的方式转移到配置文件中,交给Spring工厂来创建对象
DI (dependcy Injection) 依赖注入
Spring不仅要创建对象,还要建立类与类之间的关系,因此在控制反转的基础啊上又提出了依赖注入的概念
Set方式注入
<!--八种基本数据类型+String类型+日期类型的注入-->
<property name="name" value="xxxx"/>
<property name="age" value="18"/>
<property name="id" value="1001"/>
<property name="birthday" value="2021/12/27"/>
<property nem="price" value="19.99"/>
<!--注入数组类型的数据-->
<property name="names">
<array>
<value>xxx</value>
<value>qiqi</value>
<value>jinjin</value>
</array>
</property>
<!--注入引用类型和对象-->
<property name="userDao" ref="userDao"/>
<property name="lists">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="maps">
<map>
<entry key="aa" value="小白"/>
<entry key="bb" value="小黑"/>
<entry key="cc" value="小芬"/>
</map>
</property>
<property name="props">
<props>
<prop key="url">jdbc:mysql://localhost:3306</prop>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
注意:引用类型使用ref属性注入,基本类型使用value属性注入
构造注入
<constructor-arg index="0" name="id" value="1"/>
<constructor-arg index="1" name="name" value="xxx"/>
<constructor-arg index="2" name="age" value="18"/>
<constructor-arg index="3" name="tels">
<array>
<value>111223456</value>
<value>223456445</value>
<value>333456445</value>
</array>
</constructor-arg>
自动注入
autowire="byName"
根据注入的属性名与配置文件中的bean的id匹配,一致则注入,不一致报错autowire="byType"
根据注入的属性类型,与配置文件中的类型匹配,类型一致注入(在多个实现类时,会产生歧义)无论使用以上哪种方式注入都需要为属性提供set方法
bean的创建模式
singleton:单例 默认
在工厂中全局唯一,只创建一次
prototype:多例
全局不唯一,每次使用都会创建一个新的对象
<bean id=" " class="xxxx.userCotroller" scope="prototype | singleton">
service dao --------------> singleton
controller ---------------> propotype
bean的生产原理
原理:反射+构造方法
userDaoImpl userDao =(UserDaoImpl)Class.forName("com.tjcu.dao.UserDaoImpl").newInstance();
System.out.println(userDao);
bean的生命周期
何时创建
随着工厂启动,所有单例bean随之创建 非单例的bean,每次使用时创建
何时销毁
工厂关闭,所有bean随之销毁(注意:Spring不会负责多例bean的销毁)
bean工厂创建对象的好处
1、使用配置文件管理Java类,在生产环境中更换类的实现时不需要重新部署,修改文件即可
2、Spring默认使用单例的模式创建bean,减少内存的占用
3、通过依赖注入建立了类与类之间的关系(使Java之间关系更为清晰,方便了维护与管理)
代理
什么是代理?
代理指的是Java中的一种设计模式为什么需要代理?
很多时候除了当前类能够提供的功能外,我们还需要补充一些额外功能代理的作用?
代理对象可以在客户和目标对象之间起到中介作用,从而外目标对象增添额外的功能
静态代理的开发
目标类|对象(target):被代理类称之为目标类|或者被代理的对象称之为目标对象
目标对象
开发代理的原则:代理类和目标类的功能一致且实现相同的接口,同时代理类中依赖于目标类对象
开发静态代理类
//静态代理类
//开发原则:代理类和目标类实现相同接口,依赖于真正的目标类
public class UserServiceStaticProxy implements UserService {
//真正的目标类 //target 原始业务逻辑对象
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void save(String name) {
try {
System.out.println("开启事务");
userService.save(name);//调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void delete(String id) {
try {
System.out.println("开启事务");
userService.delete(id);//调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void update() {
try {
System.out.println("开启事务");
userService.update();//调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public String findAll(String name) {
try {
System.out.println("开启事务");
String result = userService.findAll(name);//调用真正业务逻辑方法
System.out.println("提交事务");
return result;
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
return null;
}
@Override
public String findOne(String id) {
try {
System.out.println("开启事务");
//调用目标类方法
String one = userService.findOne(id);//调用真正业务逻辑方法
System.out.println("提交事务");
return one;
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
return null;
}
}
更改目标实现类
public class UserServiceImpl implements UserService {
@Override
public void save(String name) {
System.out.println("处理业务逻辑,调用DAO~~~");
}
@Override
public void delete(String id) {
System.out.println("处理业务逻辑,调用DAO~~~");
}
@Override
public void update() {
System.out.println("处理业务逻辑,调用DAO~~~");
}
@Override
public String findAll(String name) {
System.out.println("处理业务逻辑,调用DAO~~~");
return name;
}
@Override
public String findOne(String id) {
System.out.println("处理业务逻辑,调用Dao~~~");
return id;
}
}
配置静态代理类
<!--配置目标类-->
<bean id="userService" class="staticproxy.UserServiceImpl"></bean>
<!--配置代理类-->
<bean id="userServiceStaticProxy" class="staticproxy.UserServiceStaticProxy">
<!--注入目标对象-->
<property name="userService" ref="userService"/>
</bean>
调用代理方法
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userServiceStaticProxy = (UserService) context.getBean("userServiceStaticProxy");
userServiceStaticProxy.save("小黑");
问题:在开发中我们书写的不仅仅是一个业务层,我们的业务层会有很多,如果为每一个业务类开发一个静态代理类,造成业务代码冗余的问题
为业务层在运行过程中动态创建代理类,通过代理类解决业务层中业务代码冗余的问题
动态代理的原理
public class TestDynamicProxy {
public static void main(String[] args) {
final UserService userService = new UserServiceImpl();
//参数1:当前线程类加载器
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
//参数2:生成代理类的接口类型
Class[] classes = new Class[]{UserService.class};
//参数3:通过代理类对象调用方法时会有限进入参数三中的invoke方法
//返回值就是动态代理对象
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(contextClassLoader, classes, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try{
System.out.println("开启事务");//附加操作
Object invoke = method.invoke(userService, args);
System.out.println("提交事务");//附加操作
return invoke;
}catch (Exception e){
System.out.println("回滚事务");//附加操作
}
return null;
}
});
userServiceProxy.save("小黑");
}
}
AOP[面向切面编程]
AOP(Aspect Oriental Programing)面向切面的编程
通知(Advice):除了目标方法以外的操作都称之为通知
切入点(PointCut):要为哪些类中的哪些方法加入通知
切面(Aspect):通知+切入点
通知分类
步骤
<!--1.引入依赖 pom.xml
2.开发通知类 选择哪一种通知
MethodBeforeAdvice 前置通知
MethodInterceptor 环绕通知
AfterReturningAdvice 返回后通知
ThrowsAdvice 异常通知
MyAdvice implements 通知接口{.....}
自定义通知类:用来完成额外功能
public class MyAdvice implements MethodBeforeAdvice {
@Override //参数1:当前调用的方法对象 //参数2:当前调用方法对象的参数 //参数3:目标对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("目标方法名: "+method.getName());
System.out.println("目标方法的参数: "+objects);
System.out.println("目标对象: "+o.getClass());
}
}
3.组装切面
a.引入aop命名空间
b.管理通知-->
<!--管理通知类-->
<bean id="myAdvice" class="before.MyAdvice"/>
c.配置切面
<aop:config>
<!--配置切入点 id:切入点唯一标识 expression:切入点表达式 确认要为哪些类添加额外功能
execution()切入点表达式的一种 精确到要添加额外功能的方法
-->
<aop:pointcut id="pc" expression="execution(* before.UserServiceImpl.*(..))"/>
<--advice-ref:已有通知的id pointcut-ref:已有切入点的id-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="pc"/>
</aop:config>
<!--4.启动工厂测试-->
//启动工厂
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("before/spring.xml");
//获取组件
UserService userSerivce = (UserService) context.getBean("userService");
System.out.println(userSerivce.getClass());
//调用方法 通过代理类调用方法
userSerivce.save("小黑");
通知[接口]
1.前置通知(MethodBeforeAdvice)的使用
2.环绕通知(MethodInterceptor)的使用
3.返回后(后置)(AfterReturningAdvice)通知
4.异常(ThrowAdvice)通知
目标方法正常执行结束,执行后置通知
如果目标方法执行出现异常,才执行异常通知
切入点表达式
作用:通知Spring框架要为哪些类生成动态代理对象
1.execution方法级别的切入点表达式
execution[访问修饰符]返回值 包名.类名.方法名(参数表) [ ]中的内容可以省略
注意:书写访问修饰符 返回值 包名 类名 方法名时 都支持通配 *表示任意
书写参数表时支持通配 ..表示任意
execution常用方式:
注意:方法级别的切入点表达式尽可能精准,否则程序可能出现异常
2、within类级别的切入点表达式 精确到类
1.语法
within(包.类)书写包名 类名时 也支持通配 *表示任意
2.示例
within(com.baizhi.service.*)
包: com.baizhi.service
类: 所有类中所有方法不关心返回值和参数
within(com.baizhi.service.UserServiceImpl)
包: com.baizhi.service
类: UserServiceImpl类中所有方法不关心返回值和参数
3、注意[自定义注解]
注意:within的效率高于execution表达式,推荐使用within表达式