技术体系架构
单一架构:一个项目一个工程,导出为一个war包,在一个tomcat上运行,也叫all in one。主要技术框架为Spring,SpringMVC,Mybatis
分布式架构:一个项目拆分成多个模块,每个模块是一个IDEA中的一个moudle,每个工程运行在自己的Tomcat上,模块间可以互相调用,每个模块内部都可以看成是一个单一架构的应用。主要技术框架为SpringBoot(SSM),SpringCloud,中间件等
框架式一个集成了基本结构、规范、设计模式、编程语言和程序库等基础组件的软件系统。
使用框架的优点:提高开发效率、降低开发成本、提高应用程序稳定性、提供标准化的解决方案
使用框架的缺点:学习成本高、可能存在局限性(应用需求超过架构范围)、版本变更和兼容性问题、架构风险(开发者可能出现设计和架构缺陷)
框架实际上就是jar包+配置文件
广义的Spring是以Spring Framework为基础的Spring全家桶;狭义的Spring是Spring Framework
Spring Framework的优势:生态系统丰富、模块化设计、简化java开发、不断创新和发展
SpringIoC容器
组件管理
Spring充当组件管理的角色,Spring框架代替了原有程序员new对象和对象赋值动作
我们只需要编写元数据(配置文件)告知Spring管理哪些类组件和他们的关系即可
组件实质就是可以复用的java对象。
组件一定是对象,但对象不一定是组件,需要对象满足可复用的原则
优势:降低了组件之间的耦合性、提高了代码的可复用性和可维护性、方便了配置和管理、交给Spring管理的对象可以享受Spring框架的其他功能,如AOP声明事务管理等
容器
普通容器:仅做存储,如数组、集合等
复杂容器:从全生命周期管理组件,不仅存储组件,还管理组件之间的依赖关系,并且创建和销毁组件。
SpringIoC容器通过配置元数据获取有关实例化、配置和组装组件的指令。配置元数据以XML、java注释或java代码形式表现。
SpringIoC容器由接口和实现类组成
容器接口:BeanFactory:提供了一种高级配置机制,能管理任何类型对象
ApplicationContext:是BeanFactory的子接口,扩展了一些功能
容器实现类:ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
WebApplicationContext
AnnotationConfigApplicationContext
IoC控制反转
将控制权从应用程序转移到IoC容器中
在IoC容器中完成对象实例化
DI依赖注入
通过配置文件或注解方式,由IoC容器对象进行依赖注入
三种注入方式:构造函数注入、Setter方法注入和接口注入
IoC实现
1.编写配置信息
2.实例化IoC对象并指定配置信息
3.在java代码中使用getBean获取组件并使用
4.close销毁容器
实例化对象的方法:构造函数实例化(有参、无参构造器)、工厂模式实例化(静态,非静态工厂)即使用方法内部构造对象
无参构造组件(要求当前类必须包含无参构造函数):<bean id class=类全限定符 />,声明多个同类不同id组件默认单例模式,为两个组件对象
静态工厂创建组件:<bean id class=工厂类全限定符 factory-method=静态工厂方法 />
非静态工厂创建组件:<bean id=工厂对象 class=工厂类全限定符 /><bean id factory-bean=工厂对象(id) factory-method=工厂方法 />
<bean id class=类全限定符 init-method = 该类初始化方法 destroy-method= 该类销毁方法/>容器会在对应时间结点调用方法
调用销毁方法需要使用close()
DI配置
基于构造函数的依赖注入或基于Setter的依赖注入
引用和被引用的组件必须全部在IoC容器中
IoC容器是高级容器,内部有缓存动作,会先创建所有对象再进行属性赋值(引用和被引用的类的代码顺序不重要)
通过单个构造参数注入:1.首先将引用和被引用的组件放在IoC容器中,有参构造器使用双标签<bean id class=类全限定符><constructor-arg value=属性值 ref=引用的beanid></bean>value和ref二选一
通过多个构造参数注入:在bean标签中写多个<constructor-arg value=属性值 ref=引用的beanid>,value赋值可以根据构造器参数顺序赋值或根据构造参数名赋值,添加name属性,还可以通过参数下角标指定填写(参数顺序index,将参数列表当做从0开始的数组)
通过Setter方法注入:在bean标签中写多个<property name=属性名(调用setter方法的名,只是去掉了前面的set和第一个首字母小写) value=属性值 ref=引用的beanid>value和ref二选一
property标签实质寻找的是set方法
IoC容器创建
方法1:直接创建容器指定配置文件。ApplicationContext xxx = new 实现类(配置文件)
方法2:先创建空容器对象,再使用setConfigLocations()指定配置文件,再refresh刷新。(源码的配置过程)
IoC组件读取
方法1:通过beanId获取,getBean(beanid),返回值是Object对象,需要强转(不推荐)
方法2:通过beanId获取同时指定Class,getBean(beanId,class)
方法3:直接通过Class获取,getBean(class)。同一个类型在一个IoC容器中只能有一个组件
IoC配置一定是实现类,但是也可以通过接口获取值
只要instanceof ioc容器类型 == true就可以获取到
Bean标签
在IoC中bean标签对应的信息转成Spring内部BeanDefinition对象,在该对象内包含定义的信息
容器可以根据BeanDefinition对象反射创建多个bean对象,创建个数由作用域scope指定
scope=singleton为单例,bean对象始终为单实例,创建时机是容器初始化时,默认该选项
scope=prototype为多例,bean对象在容器中有多个实例,获取bean时创建
在WebApplicationContext环境下还有:
scope=request,请求范围内有效的实例
scope=session,会话范围有效的实例
factoryBean
factoryBean是一个标准化工厂,可以通过getObject方法进行组件实例化,要给创建的对象赋值需要在factoryBean中声明属性并赋给创建出的对象
组件实例化方式:构造函数<bean class类>、工厂模式<bean 工厂类 factory-method方法名>、factoryBean标准化工厂,通过getObject编写自己的实例化逻辑,<bean 工厂类>
getObject()返回工厂创建对象的实例并存入IoC容器
isSingleton()如果返回单例则为true,否则为false
getObjectType()返回getObject()方法返回的对象类型,不知道类型则返回null
使用场景:代理类的创建、第三方框架整合、复杂对象实例化
工厂也会被放入IoC容器,名字为@id
FactoryBean和BeanFactory区别
FactoryBean是Spring中一种特殊的组件,可以在getObject方法进行组件实例化,是一种能生产其他组件的组件。FactoryBean在容器启动时被创建,可以自定义任何所需的初始化逻辑,生产出定制化的bean
BeanFactory是Spring框架的基础,作为顶级接口定义了容器的基本行为,如管理bean生命周期,配置文件的加载和解析,bean的装配和依赖注入等。BeanFactory接口提供了访问bean的方式,如getBean方法获取指定的bean实例。它可以从不同来源获取bean定义,并将其转换为bean实例。同时BeanFactory还包含很多子类如ApplicationContext,提供了额外的强大功能
基于XML配置IoC
通过IoC配置:
1.bean标签中引入DruidDataSource类,并且赋属性url,driverClassName,username,password
2.bean标签中引入JdbcTemplate类,ref=上面创建的类id
还可以通过外部文件引入(只支持.properties文件),在beans中导入xmlns:context=文件路径,之后<context:property-placeholder location="classpath:文件名"(可以有多个class:文件名,用逗号隔开)>。在赋属性时可以直接使用文件中的类.属性名
通过IoC读取:1.通过实现类实例化创建IoC容器 2.通过getBean获取jdbcTemplate组件 3.进行数据库操作
update操作:
数据库操作中使用String类型编写sql语句
参数一:value中可以使用?代替值,但不能代替关键字和容器名;
参数二:Object传入占位符的值顺序从左开始;
返回值为int影响函数
select操作:
数据库操作中使用String类型编写sql语句
参数一:sql语句可以使用?
参数二:RowMapper列名和属性名的映射器接口
参数三:Object...pram可以变参数
返回值为rowMapper指定的对象
使用XML配置IoC缺点
1.注入的属性必须添加setter方法,代码结构乱;2.配置文件和java代码分离,编写不便;3.XML配置文件解析效率低
基于注解方式配置IoC
1.在类上添加IoC注解
2.告诉SpringIoC容器在哪些包下添加了IoC注解
注解
注解本质没有区别,只是用于后期区分
配置扫描包
扫描基本配置
指定了包中所有的类:<context:compoent-scan base-package="包名,包名..." />
排除包中某类注解:在context双标签中加<context:exclude-filter type="annotation" expression="包名">
指定包并指定注解:在context双标签中加<context:include-filter type="annotation" expression="包名">,需要在标签头中加use-default-filters="false"将包中注解全部排除
引入外部文件
<context:property-placeholder location="classpath:文件名"(可以有多个class:文件名,用逗号隔开)>
设置beanName
在注解后面加(value="name"),value=可以省略,直接写成("name")
默认name是类首字母小写加注解标签名
注解方式管理生命周期
周期方法命名为public void无参方法
@PostConstruct为注解指定初始化方法
@PreDestroy为注解指定销毁方法
单例模式不会管理销毁方法
注解方式控制作用域
@Scope(scopeName = COnfigurableBeanFactory.SCOPE_SINGLETON)单例作用域,其他作用域只需要修改最后部分即可
引用类型自动装配(DI)
@Autowired注解标识在成员变量、构造器、set方法上,首先在ioc容器中查找符合类型的组件对象,然后设置给当前属性DI
佛系装配
@Autowired中默认boolean required() default true,则组件必须存在,否则报错。佛系装配指修改为@Autowired(required=false),组件是否存在不影响
使用佛系装配会导致后续调用该组件返回空指针报错,一般不推荐使用
找到多个组件
第一种方法可以修改成员变量名为精确的要选择的组件id
第二种方法通过@Qualifier注解成员变量,设置value指定该成员变量装配组件
@Qualifier不能单独使用,必须配合@Autowired
@Resource注解
使用@Resource(name=“组件名”)相当于同时使用了@Qualifier(value=“组件名”)和@Autowired(required=true)
使用该注解需要导入包
对基本数据类型赋值
方法1:直接在声明变量时赋值
方法2:使用注解@value(“值”)赋值
使用value注解通常为了读取外部数据:
1.添加注解;2.扫描包;3.引入外部文件;4.将“值”写为"$(properties文件中变量名)"
第三方类配置
对于第三方只读文件仍需要使用xml进行配置
使用注解方式配置IoC缺点
1.自定义类可以使用注解方式,但第三方文件仍然使用XML方式配置
2.XML格式解析效率低
基于配置类方式配置IoC
完全注解开发:通过注解替代xml中的配置标签,完全取代xml配置
配置方法
第一步:配置类前加注解@Configuration,表示该类为配置类
第二步:配置包扫描:@ComponentScan(“包名”),value=可以省略
第三步:配置外部文件:@PropertySouce("classpath:文件名")
第四步:声明第三方依赖bean组件。
在配置类中声明方法,方法名为bean id;
方法返回值类型为bean组件的类型他的接口和父类;
方法体可以自定义实现过程,对每个property,设置private字符串,在前面用@value注解赋值,可以设置为配置类中全局变量或在方法形参列表中仅在方法中使用。通过set方法设置各种property。返回return创建好的对象。最后在配置类前加@bean注解即可。
beanName默认为方法名,可以用name或value属性设置beanname
指定周期方法:
方法一:使用原有注解PostConstruct+PreDestroy
方法二:使用@bean属性initMethod和destroyMethod指定
指定作用域:
@Scope(scopeName = COnfigurableBeanFactory.SCOPE_SINGLETON)
引用其他组件:
方法一:其他组件也是@bean方法,可以直接使用setbean名调用,参数中使用(方法())
方法二:将要调用的组件放入形参列表中,使用时直接调用。该方法调用要求必须有被调用组件在容器中,否则抛出错误。如果有多个同样组件需要形参名=beanId,注入多个使用逗号隔开
创建IoC容器:
ApplicationContext xxx = new AnnotationConfigApplicationContext(配置类)或者先创建空容器对象,再使用register()指定配置类,再refresh刷新。
@import注解
导入多个@Configuration,可以在其中一个前加@import(value=(其他标签)),导入这个@Configuration即可导入所有的
@SpringJUnitConfig注解
不用再主动创建IoC容器,需要的bean会在测试类中自动装配
@SpringJUnitConfig(locations = xml文件,value= 配置类)
可以直接@Autowired注入组件,直接使用
Spring AOP面向切面编程
AOP是对OOP的完善和补充,AOP将代码中重复的非核心业务提取到一个公共模块,最终再利用动态代理技术横向插入各个方法之中,解决非核心代码冗余问题
代理模式
在调用目标方法时不直接调用,而是借助代理类间接调用,为了让不属于目标方法核心逻辑的代码从中剥离解耦
静态代理
将被代理的目标对象声明为成员变量,附加功能由代理类中的代理方法来实现,通过目标对象来实现核心业务逻辑
代码需要提前编写,使用时局限性大,为了适应多种需求需要编写大量冗余代理类
动态代理
java原生代理
目标类必须有接口,接口生成一个代理类。返回的代理对象是目标类的兄弟,给接口创建另外的实现类,接值要用接口而不能用目标类接
生成代理对象需要传入类加载器,目标类接口和要执行的代理动作
调用代理的都会执行invoke方法,方法需要代理对象,目标方法(需要重写的方法)以及执行方法的参数args
Cglib动态代理
不要求目标类有接口,直接根据目标类生成一个子类对象,可以直接用原类接值
应用场景
日志记录(在不同时期发送日志),事务管理(开启和关闭事务冗余代码),安全控制(在执行每次操作前检查权限),性能监控(计算执行时间),异常处理,缓存控制,动态代理
术语名词
横切关注点:非核心代码,附加业务的添加位置
通知(增强):横切关注点中使用的实现方法。分为前置通知,返回通知(执行完成后),异常通知,后置通知(方法最终结束后),环绕通知(try catch finally)
连接点:可以被插入附加业务的位置
切入点:添加了附加业务的连接点
切面:切入点和业务的结合
目标:被代理的目标对象
代理:目标对象应用通知后创建的代理对象
织入:把通知应用到目标上,生成代理对象的过程,Spring在运行时织入
AOP实现
AOP作用对象必须在IoC容器中,创建完成代理对象,将代理对象也放入容器
首先正常写核心功能代码,之后将非核心代码插入
第一步:编写核心代码
第二步:编写非核心代码
有几个插入位置就写几个方法
第三步:使用注解分别用前置@before,后置@AfterReturning,异常@AfterThrowing,最后@After,环绕@Around注解标明方法
注解位置try{
前置
目标方法
后置
}catch(){
异常
}finally{
最后
}
第四步:配置切点表达式,选中插入的方法和切点@before("execution(包.类.方法(参数类型))")
第五步:用@Component加入容器,添加@Aspect注解配置切面
第六步:开启Aspect注解的支持,导入包
第七步:在配置类中添加非核心代码包的扫描
在xml配置文件中开启注解支持:<aop:aspectj-autoproxy />
在配置类中开启注解支持:@EnableAspectJAutoProxy
获取通知中的信息
1,获取通知中目标方法的信息,在目标方法中添加一个形参对象JoinPoint
获取类信息通过getTarget().getClass().getSimpleName()
获取方法名getSignature().getName()
获取方法的访问修饰符:首先获取int类型getSignature().getModifiers();然后使用Modifier.toString()方法转换为字符串形式
2,获取@AfterReturning中返回值,在目标方法中添加一个Object result对象形参,在注解value中添加returning=“result”
3,获取@Afterthrowing异常信息在目标方法中添加一个Throwable throwable对象形参,在注解value中添加throwing=“throwable”
切点表达式
访问修饰符和返回值要么一起考虑,要么一起写作*
包的位置可以写出具体包路径,也可以用单层模糊service.*表示service下所有包,也可以用多层模糊com..impl,任意路径下的impl包,但是..不能开头,至少写成*..
类的名称可以写出具体类名,也可以用*模糊所有类,也可以部分模糊*impl表示以impl结尾的类
方法名语法和类一致
参数列表无参用(),有具体参数用(数据类型,数据类型,数据类型)不分顺序,模糊参数(..)有没有参数或有多个参数都可以,部分模糊(String..int)String开头,int结尾
重用切点表达式
方法1.在当前类中提取,定义一个空方法,添加注解@Pointcut(切点表达式方法),将切点方法的注解改为提取出来的方法名
方法2.创建一个存储切点的类,单独维护切点表达式,在切点位置注解中加类的全限定符.方法名
环绕通知
环绕通知创建一个单独的方法,需要再通知中定义目标方法的执行,在切点处直接写入实现方法
切面优先级
@Order(优先级值)值越小优先级越高
XML方式实现AOP
<aop:config>
声明切点标签
<aop:pointcut id ="方法名" expression="execution(切点表达式)"/>
切面配置标签
<aop:aspect ref="增强对象" order="优先级">
<aop:before method="方法名" pointcut-ref="存储切点的类">
</aop:aspect>
</aop:config>
bean标签获取
当IoC容器中有多个同类bean时,会抛出NoUniqueBeanDefinitionException异常
当bean对应的类实现了接口,并且接口只有这一个实现类,则通过接口类型和类获取bean获取到的是同一个对象
当接口有多个实现类,接口所有实现类都放入IoC容器中,根据接口类型获取会报NoUniqueBeanDefinitionException异常,根据类获取bean则正常
接口的实现类创建了切面类,并添加了通知,则通过接口类型获取bean正常,无法通过类获取bean。因为应用了切面后,在IoC容器中的是代理类的对象,而目标类没有被放入IoC容器中(jdk代理)
声明一个类,对其应用切面类并添加通知,则能通过类获取bean。因为cglib代理继承了该类
声明式事务Spring-tx
编程式事务指手动编写程序来管理事务。编程式事务灵活性高,但是编写大量事务控制代码容易出现问题,对代码可读性和可维护性有一定影响。细节未被屏蔽,代码复用性也不高
声明式事务指通过注解或XML配置的方式控制事务的提交和回滚
事务管理器
spring-tx包含声明式事务实现的基本规范
spring-jdbc包含DataSource方式事务管理器实现类DataSourceTransactionManager
spring-orm包含其他持久层框架的事务管理器实现类
最上方接口没有实质作用,中间提供了事务管理器方法,下方为具体类型的实现
事务实现
添加事务首先将事务放入IoC容器,再通过@Transactional注解开启事务
放入容器通过导入第三方配置类导入TransactionManager类,新建对象并将需要的连接池对象作为形参然后set注入,返回创建好的对象,再在配置类前面添加@EnableTransactionManager注解开启事务注解支持
在方法前加表示方法应用事务,在类前加表示类中所有方法都有事务
事务属性
只读
只有查询代码时推荐使用只读模式,可以提高查询事务的效率
@Transactional(readOnly=true)
一般在类前加只读,在类中的查询方法可以通过添加只读注解提高事务效率
超时时间
使超时的事务回滚,释放资源
@Transactional(timeout=秒数)
默认-1永不超时
如果方法和类上都有注解,方法的注解会覆盖类上的注解
事务异常
默认情况下发生运行时异常事务才会回滚,可以指定Exception异常控制所有异常回滚
@Transactional(rollbackFor=Exception.class,noRollbackFor=回退范围内某个异常不回滚)
事务隔离级别
@Transactional(isolation=隔离级别(字母全大写中间加下划线))
推荐设置读已提交
事务传播行为
方法之间互相调用行为
@Transactional(propagation)
=REQUIRED则父方法如果有事务,则加入事务,若没有事务则子方法单独创建一个事务。默认值,并且推荐使用该方法
==REQUIRES_NEW父方法无论有没有事务,字方法都新建独立事务
在同一个类中调用不会生效,因为Spring框架中使用代理模式实现事务机制,同类调用不使用代理,而是通过对象方法调用,注解设置不会被代理捕获,不会产生事务传播行为