Spring总结IOC控制反转,由Spring帮管理对象
AOP面向切面编程:AOP用到的其实也是动态代理,扩展功能
DI依赖注入
一句话就是spring通过DI(依赖注入)实现IOC(控制反转)
所谓IOC控制反转:就是让Spring容器通过DI依赖注入来管理对象
而正转就是程序员来决定创建对象
这个.........
正转:由程序员进行对象的创建和依赖注入称为正转
Student stu=new Student();
stu.SetName("张三");
stu.SetAge(22);
反转:由Spring容器创建对象和依赖注入称为反转,将控制权交由Spring容器
.XML的配置方式
Spring官网:https://spring.io
重复下面向接口编程
口号:类中的成员变量设计为接口,方法的参数设计为接口,方法的返回值设计为接口,调用时接口指向实现类.好处???解耦,健壮性?可移植性?扩展性?维护性?
使用接口编程可以更灵活的面向对象,提高项目的可扩展性,可维护性.接口不关心实现类的类型,使用时接口指向实现类,切换实现类可以灵活切换功能.
记得有次面试问起设计接口好处:没有点专业术语好像挺难回答的
通俗点说,公司有个技术总监叫小王,小王工作能力很强,啥事都找小王,这样这个公司是存在问题的,耦合度太高.过于依赖.
但是换个角度想,公司缺个技术总监,有事找技术总监,而技术总监可能是小张,小李,还可能是隔壁老王.公司不依赖与某个人,而是在公司这个框架中设置了技术总监这样个职务,至于技术总监是谁对于公司而言本身不是太重要是可以被替换的.解耦
接口就是定义了一种规范.比如说JDBC。只是定义了,不管是mysql的,或者Oracle的,无论你哪个公司要用我的产品,就必须遵守我的规范,来实现我定义的接口.不然那么多驱动,那么多公司,各出各的,无法达到统一.
插线板就是接口,USB也是接口.
所谓依赖:A类中含有B类实例.在Java语言中体现为局域变量、方法的形参,或者对静态方法的调用
就说明A类对B类有依赖关系
而依赖太高是好是坏?如果一个公司太依赖于某个人,对公司而言绝对不是好事.
所以提出了解耦.如何解耦.用接口.父类引用指向子类实现
Spring框架是个容器,帮我们管理对象
IOC(控制反转):两种方式.一种是注解方式.也是我们后期开发主要干的.另外一种是.XML配置的方式
XML配置方式关于成员方法注入方式 1.set方法注入 2.构造方法注入
Set方法注入和构造方法注入两种方式里又区基本类型(包装类)注入和引用类型注入
而构造方法注入方式除了基本类型注入和引用类型注入外又细分为构造方法参数名称注入
构造方法参数索引下标注入,不指定名称和下标索引的注入和注入系统的类
我们先用.XML配置文件方式来注入值,让spring管理
开始写代码,新建个空项目
修改下JDK版本
添加完spring依赖后 刷新maven
就可以创建spring config配置文件
.XML里给对象赋值
A.使用setter注入
又分为简单类型注入和引用类型注入
简单类型注入使用value属性
引用类型注入使用ref属性
使用setter注入必须提供set方法和无参构造方法
B.使用构造方法注入
演示Spring容器通过set方法且有无参构造方法注入值
.XML写法
测试
如果value里的值和对应类型不一致,则报错 用setter方法必须提供无参构造和set方法
演示setter注入引用类型变量值
演示小demo,正式项目中Spring会接管除实体类外所有对象(三层架构,控制层,业务层,持久层对象)的创建
另外创建个项目演示客户端请求到持久层返回(三层架构模式)
现在这个项目不需要引入依赖
我们就以最原始的方式搭建三层架构
实体类
数据访问层接口
数据访问层实现类
业务层接口
业务层实现类
控制器类
测试,插入用户成功
其实以上就是三层架构的模板,就创建了一个maven项目,都不需要创建Maven项目
没有引入任何依赖,没有任何配置,但是控制器访问业务层,业务层访问持久层,都是我们自己创建对象的
下面演示让用.XML的配置让Spring接管三层架构对象
为了方便这里复制了一份
然后导入项目,修改下对应的pom.xml
现在我们通过Spring帮我们管理三层架构中的对象,需要导入Spring依赖,这里用.XML形式进行配置
4.0.0 com.hrui spring_003_sancengjiagou1.0-SNAPSHOT 8 8 junit junit4.12 test org.springframework spring-context5.2.6.RELEASE org.projectlombok lombokRELEASE compile src/main/java ***.properties src/main/resource ***.properties
添加.XML配置文件
实体类
数据层接口
数据层实现类
业务层接口
业务层接口
控制器
.XML配置
测试
下面演示Spring容器通过有参构造方法给属性注入值
常用的3种:
1.使用构造方法的参数名称进行注入值
2.使用构造方法参数的下标注入值
3.使用默认的构造方法的参数的顺序注入值
在原先创建的spring_001_demo里演示使用构造方法的参数名称进行注入值
注意.XML里配置的构造方法参数名必须和实际构造方法参数名一致
演示使用构造方法下标进行简单类型和引用类型注入值
使用构造方法中参数默认顺序注入值
演示基于注解的IOC概念(也是实际工作中使用的通过注解给属性注入值,称为自动装配)
大致分为两套注解:
1.创建对象的注解
1.1)@Component:通用注解,可以创建对象由Sprng管理,@Component("abc")可指定对象引用名称,默认类名首字母小写 下面也一样
1.2)@Controller:控制器层专用注解,可以创建控制层对象由Sprng管理
1.3)@Service:业务层专用注解,可以创建业务层对象由Sprng管理
1.4)@Repository持久层专用注解,可以创建业务层对象由Sprng管理
还有很多,不仅限与上面这些,以上都是常用的Spring框架注解 @Mapper是Mybatis注解
2.依赖注入的注解
2.1)@Value:用来给简单类型注入值
2.2)@Autowired:给引用类型注入值,从整个Bean工厂中搜索同源类型对象进行注入 就是 byType(Spring框架默认byType)
2.3)@Autowired+@Qualifier:两个注解一起使用,使用名称注入值,从整个BEAN工厂中搜索相同名称的对象进行注入 就是byName
新建一个项目演示自动装配 只需要注解+.XML包扫描即可
可指定引用名
那么从bean池取得时候需要用指定得引用名取
@Value演示(8种基本类型+String)值注入
演示引用类型注入
@Autowired 或者@Autowired+@Qualifier指定名称 byName
Spring框架默认使用byTpe就是按类型注入
按类型注入
1.被注入的类型与需要注入的类型是完全相同的类型
2.被注入的类型与需要注入的类型是父子类,实现类
演示引用类型自动装配 默认byType
演示引用类型按名称注入(即School类型有多个,一个是School本身,一个是School1继承自School)
下面这种方式也可以
下面演示基于注解改造三层架构
将原先的三层架构复制了一份修改了pom文件
导入项目
下面开始以注解方式改造三层架构
添加包扫描快捷 需要改造加注解的地方 测试 关于包扫描的几种方式,上面为了方便,就扫描了根包,其实根本应该是com 多扫描了些pojo 拆分.XML 当了解扩展 当时当spring配置文件被拆成多个时候, 读取时候怎么办 spring关于配置文件的整合策略 1.单个读取(导入) 2.批量导入 试试 用total批量导入 测试是,我只加载total.xml 支持*号的批量导入 另外 面向切面编程(AOP)AOP底层就是通过动态代理模式实现的。JDK动态代理和CGLIB动态代理 大概学习流程 手写AOP框架:业务为图书购买业务 1)第一个版本:业务和切面紧耦合在一起,没有拆分 2)第二个版本:使用子类代理的方式拆分业务和切面 3)第三个版本:使用静态代理拆分业务和切面.业务和业务接口已拆分,此时切面紧耦合在业务中 4)第四个版本:使用静态代理拆分业务和业务接口,切面和切面接口. 5)第五个版本:使用动态代理完成第四个版本的优化 新建个项目 第一个版本单引入单元测试,其他暂时不用 第二个版本:使用子类拆分业务和切面 就这么回事 使用第三个版本 第三个版本:使用静态代理拆分业务和切面.业务和业务接口已拆分,此时切面紧耦合在业务中 还记得什么是静态代理吗? 有接口,有实现类(目标对象),代理类也得实现相同接口,在代理类中完成目标对象方法的扩展 接口 目标对象 代理对象 测试 为何说静态代理能灵活切换目标对象功能,但不能进行功能的灵活处理(使用动态代理解决此问题,一会代码演示,即接口现在只有buy方法,但是当有其他方法时候不能灵活对功能扩展) 多一个业务 以上版本切面是死的,只能是事务,那如果我需要日志或其他切面呢 4)第四个版本:使用静态代理拆分业务和业务接口,切面和切面接口. 套多个切面 你品,你细品,帅呀,套娃,把他爹都套来了(重点掌握) 以上只是静态代理对目标对象的灵活切换,但是满足不了功能的灵活切换,那么就使用动态代理,发现把自己套进去了,分不清JDK动态代理和CGLIB代理了 Mybatis学习笔记---代理_fhrui的博客-CSDN博客 第5个版本将静态代理改成动态代理 也就是BooKServiceImplProxy改成动态代理,其他拷贝到proxy05 测试 感受动态代理好处,在接口新增业务 注意可以做个空实现{}什么都不写,那个需要实现功能就去重写,这里为了演示,随便了 上面讲的是AOP原理 下面了解Spring原生写好的AOP使用,我们需要了解如何去使用就好,Spring已经写好,我们知道用就行 曾经学AOP时候的笔记 这里注意下SpringBoot中不需要加@EnableAspectJAutoProxy,默认开启,默认使用CBLIB代理 一般AOP最后会用异步请求方式去处理(如果影响了客户端体验的情况下) SpringBoot中做法很简单,配置类上加@EnableAsync开启线程池 需要异步请求方法上加@Async告诉线程池,看到此注解,另开线程进行处理 @Autowired与@Resource异同: @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
@Component
//虽然标识了该类为AOP切面 但是Spring容器默认不能识别切面注解,需要手动配置
//需要在配置类SpringConfig里加上注解@EnableAspectJAutoProxy@Aspect
public class SpringAOP {
//1.定义before通知
//@Before("bean(deptServiceImpl)")//扫描的是一个类 因此该类里所有方法都被扩展到了
//@Before("within(com.jt.service.DeptServiceImpl)")//和上面效果一样
//@Before("execution(* com.jt.service.DeptServiceImpl.add*())")/
@Before("pointcut()")
public void before(JoinPoint joinPoint){//前置方法一般作用获取参数,方法名,等等
System.out.println("目标对象的Class类对象: "+joinPoint.getTarget().getClass());
System.out.println("获取目标方法的方法签名: "+joinPoint.getSignature());
System.out.println("获取目标对象的类名: "+ joinPoint.getSignature().getDeclaringTypeName());
System.out.println("获取目标对象方法名: "+ joinPoint.getSignature().getName());
System.out.println("获取目标方法参数: "+ Arrays.toString(joinPoint.getArgs()));
System.out.println("我是before通知");
}
//1.定义一个切入点
@Pointcut("@annotation(com.jt.anno.Cache)")
public void pointcut(){
}
//如果每个通知前都加个切入点表达式 那么也太冗余了 因此我们可以定义个切入点 其他通知都围绕切入点
//@BafterReturning("@annotation(com.jt.anno.Cache)")
@AfterReturning(value="pointcut()",returning="result")
public void afterReturning(JoinPoint joinPoint,Object result){//这里注意 如果有需要用到JointPoint参数 那么必须放在第一个位置 不用可以去掉
System.out.println("目标返回值结果是: "+result);
System.out.println("我是AfterReturning的通知");
}
@AfterThrowing(pointcut = "pointcut()",throwing="e")//当目标方法执行时,抛出异常时,可以用AfterThrowing记录
public void afterThrowing(Exception e){
System.out.println("获取目标异常信息: "+e.getMessage());
System.out.println("获取目标异常类型: "+e.getClass());
System.out.println("我是AfterThrowing的通知,出现异常了");
}
@After("pointcut()")
public void after(){
System.out.println("我是After的通知");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){//注意多参情况ProceedingJoinPoint要放第一位
Object result = null;
try {
System.out.println("环绕通知开始");
//1.执行下一个通知 2.执行目标方法 3.接收返回值
Long start = System.currentTimeMillis();
result = joinPoint.proceed();
Long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕通知结束");
return result;
}
}
@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用。
@Resource(属于J2EE规范),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。



