(一)spring初识
1.spring的用途
spring是一个对象容器(BeanContainer),spring框架中管理对象(控制层对象,服务层对象,mapper代理对象,非三层对象)。
IOC(控制反转):原来我们自己new,用了spring后,由spring来通过反射创建对象,并保存对象在BeanFactory对象中。
2.为什么要把对象注册到spring容器中
- 解耦:把控制层,服务层,持久层进行解耦,每层都依赖下层的抽象接口;
经典MVC架构目的就是为了层与层之间解耦,达到下层变动上层不变,但如果没有第三方容器存在的话,仍然需要开发者自身来在上层把依赖的下层对象实例化。
把UserServlet,UserServiceImpl,UserDaoImpl,DruidDataSource都放在容器中,由spring来为这些bean对象中依赖的变量去注入值(根据变量名或变量类型去容器中查找对应的bean)
-
aop:面向切面编程。
spring容器管理bean对象,spring拿着对象的引用,就能够在运行期间通过动态代理模式,来统一的对某些对象进行扩展完善。
3.入门spring
步骤:
1.加spring的依赖jar包
2.向spring中注册bean,注册bean的方法很多
3.从spring中获取bean
-
1.加spring包
spring的四个核心包,缺一不可:beans,context,core,expression
一个apache的commons-logging.
-
2.向spring中注册bean
创建spriing.xml文件,通过bean标签注册bean。
<bean id="userService" class="com.javasm.sys.service.SysuserServiceImpl"></bean>3.从spring中获取bean
-
3.从spring中获取bean
1.加载spring.xml,创建spring的容器对象ApplicationContext ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); 2.从spring中获取bean ISysuserService userService1 = (ISysuserService) ac.getBean("userService"); ISysuserService userService =ac.getBean(ISysuserService.class); 3.注意点: bean对象什么时候创建:容器初始化时; bean对象创建几次:一次,对象默认是单例的。 bean类中必须有无参构造:因为spring内部通过反射调用无参构造实例化对象
4.了解下spring容器接口的结构
BeanFactory
ApplicationContext
ClasspathXMLApplicationContext:加载类路径下的xml文件初始化容器
FilePathXmlApplicationContext:加载文件路径下的xml文件初始化容器
AnnotataionConfigApplicaitonContext:加载类文件初始化容器
编码设计原则:
单一原则:
开闭原则:代码对于添加是开放的;对于修改是封闭的。
(二)spring 理解
1.作用
1.1 spring是一个第三方的bean容器框架,也称为ioc容器。
1.2 当spring拿到对象的引用后,即可以通过动态代理模式,为bean对象的方法进行扩展改进(统一加入事务控制,统一加入日志组件,统一进行异常处理)。
2.使用
2.1 加入spring的4个核心包。1个commons-loggings日志接口包
2.2 创建spring的配置文件,配置文件用来注册bean(控制层bean,服务层,持久层,管理器对象)
2.3 实例化容器对象ApplicationContext–>ClasspathXmlApplicationContext,容器对象应该是全局唯一的对象。
2.4 使用容器对象,获取bean,
2.4.1 按照名称获取
getBean("bean的id")
2.4.2 按照类型获取
getBean(Class clz)
3.概念
3.1 ioc:控制反转
对象的管理由应用自身维护,交给独立第三方的容器来管理对象(创建,状态,销毁,对象之间的依赖关系)
主要用在无源码的类上。比如DruidDataSource,jar包里的类要注册容器
<bean id="userController" class="com.javasm.sys.controller.SysuserController" lazy-init="true" init-method="initData" destroy-method="closeData" scope="prototype|singleton"></bean>
注意点:
-bean对象默认是在容器初始化时实例化的,可以通过lazy-init指定延迟初始化bean。
-class不能写接口名。
-id不能同名。
-spring本身是可以存储同类型的对象,在获取bean的时候注意获取按照类型获取bean,不能按照接口类型。
-对于特殊的bean对象(DruidDataSource),可以通过init-method或destroy-method指定初始化与释放资源。
-spring管理的bena对象默认是单例状态,可以通过scope="prototype"修改对象的单例为原型模式,每次getBean得到的是一个新对象。
主要用在复杂对象的注册
工厂类:是一种设计模式,用来创建对象使用。
针对复杂对象的构建,需要用到工厂实例化bean,比如SqlSessionFactory,无法直接new
<bean id="factoryBean" class="com.javasm.factory.MySqlSessionFactoryBean" factory-method="类中的静态方法名"></bean>
主要用在复杂对象的注册
<!--先实例化工厂对象-->
<bean id="factoryBean" class="com.javasm.factory.MySqlSessionFactoryBean"></bean>
<!--再调用实例工厂对象的方法,把方法的返回值注册到容器中-->
<bean id="sqlSessionFactory" factory-bean="factoryBean" factory-method="createSqlSessionFactory"></bean>
主要用在自己编写的类上。
<!--在类上加注解:@Controller,@Service,@Repository,@Component-->
<context:component-scan base-package="com.javasm"></context:component-scan>
注意点:
-包扫描需要加入spring-aop.jar。
-bean的id默认是类名首字母小写,可以指定注解的value属性自定义id名。
-这四个注解原则上是分别注解控制层,服务层,持久层,非三层的类;本质上四个注解效果一样。学到springMVC控制层框架,这个框架的Controller独立识别。
3.2 DI:依赖注入
<bean id="userController" class="com.javasm.sys.controller.SysuserController">
<property name="属性名" ref="bean的id名"></property>
</bean>
注意点:
-类中要注入值的属性必须有set方法
-如果要注入的值是容器中的一个bean对象的话,使用ref属性,ref=“bean的id”
-如果要注入的值是一个简单类型(String,包装类,基本类型,Date)的话,使用value属性,value=“值”
<bean id="userController" class="com.javasm.sys.controller.SysuserController">
<constructor-arg name="sysuserService" ref="userService"></constructor-arg>
<constructor-arg name="str" value="张三"></constructor-arg>
</bean>
注意点:
-index与name属性二选一
-ref与value属性二选一
为bean对象中的List,数组,set,Map属性赋值
property的list,array,set,map子标签
与包扫描进行bean注册一起使用
<!--在类上加注解:@Controller,@Service,@Repository,@Component-->
<!--在属性上加注解:@Autowired,@Resource-->
<context:component-scan base-package="com.javasm"></context:component-scan>
//Autowired:先按照类型找,如果按照类型无法确定bean对象;再按照变量名去找。
//Resource:先按照变量名找,找不到再按照类型找。建议使用。
总结ioc与di:
通常情况下自己编写的类使用包扫描注册bean(@Controller,@Service,@Repository,@Component),使用自动装配来维护bean的依赖关系(@Autowired,@Resource)。
通常情况下jar包中类使用bean标签注册对象(),使用properties标签来维护bean的依赖关系
4.spring与junit整合
//1.加入spring-test.jar
//2.在测试类上加runwith,ContextConfiguration两个注解,初始化容器,并把测试类也注册到容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:componentScan.xml")
public class Test4_junit{
@Resource
private GoodsController gc;
@Test
public void ddd(){
gc.xxxx();
}
}
5.了解xml文件解析
服务端进行xml解析,使用dom4j;在移动端进行xml解析,使用pull;
//1.加入dom4j.jar
//2.关于dom4j组件的的类,重点记忆一个类SAXReader
SAXReader reader = new SAXReader();
Document doc = reader.read(InputStream in);
properties文件解析
Properties p = new Properties()
p.load(InputStream in)
6.其它常用标签
1.import标签,导入其它spring风格的xml文件
<import resource="classpath:factory.xml"></import>
2.context:property-placeholder标签,导入类路径下的properties文件到spring容器
<context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true"></context:property-placeholder>
常见异常:
1.在按照类型获取bean的时候,指定类型有多个bean对象存在。
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.javasm.sys.service.ISysuserService' available: expected single matching bean but found 2: userService,userService2
2.容器创建失败,会告诉哪个bean创建异常,一般都是bean中属性注入出问题,或者没有无参构造
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'goodsController': Unsatisfied dependency expressed through field 'sysuserService';
3.加载jdbc.properties文件时,文件中的key不能单个单词,获取不到
4.当加载多个properties文件时,Could not resolve placeholder 'jdbc.url' in value "${jdbc.url}"
注意点:
架构层面的对象(mvcdao)交给spring,业务层对象(数据库表对应的实体类)是不交给spring
(三)spring 须知
1.通过类配置bean
在ssm框架中,使用xml配置的方式;在springboot框架中,推荐使用类配置方式。
1.添加spring的核心包(4个core,1个日志,1个aop,1个test)
2.创建一个类配置文件,类上加@Configuration
3.加载类配置文件初始化容器,new AnnotationConfigApplicationContext(Class clz)
在类配置文件中注册bean:
- 包扫描
@Configuration
@ComponentScan("com.javasm")//6个注解
public class ContextConfig {
}
- bean注解
@Bean(initMethod = "init",destroyMethod = "close")
public DataSource initDruid(){
DruidDataSource ds = new DruidDataSource();
ds.setUrl("jdbc:mysql://127.0.0.1:3306/704b");
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUsername("root");
ds.setPassword("root");
ds.setInitialSize(2);
return ds;
}
bean对象状态修改:
- 单例改原型
//bean对象id默认是类名首字母小写
@Controller
@Scope("prototype")
public class SysuserController {
}
//bean对象的id默认是方法名
@Bean(initMethod = "init",destroyMethod = "close")
@Scope("prototype")
public DataSource initDruid(){
DruidDataSource ds = new DruidDataSource();
ds.setUrl("jdbc:mysql://127.0.0.1:3306/704b");
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUsername("root");
ds.setPassword("root");
ds.setInitialSize(2);
return ds;
- 指定初始化与销毁方法
自定义的类:
@PostConstruct
public void init(){
System.out.println("初始化方法");
}
@PreDestroy
public void close(){
System.out.println("销毁方法");
}
bean标签注册的类:
@Bean(initMethod = "init",destroyMethod = "close") @Scope("prototype")
- 包含其他配置类:
@Import(DaoConfig.class)
- 加载properties文件
@PropertySource(value="classpath:jdbc.properties",ignoreResourceNotFound = true)
- 通过bean注解注册的bean注入依赖对象
@Bean
public SysuserController createUserController(ISysuserService sysuserService){
SysuserController sysuserController = new SysuserController();
sysuserController.setSysuserService(sysuserService);
return sysuserController;
}
总结:
@Configuration 声明一个类是配置类
@ComponentScan 开启包扫描注解(6个核心,import,ProeprtySource,PostConstruct,PreDestroy...)
@PropertySource 加载外部资源文件
@Import 导入其它配置类
@Bean 注册bean对象到容器
@Scope 指定bean的单例原型状态
@PostConstruct 指定bean对象的初始化方法
@PreDestroy 指定bean对象的销毁方法
2.aop概念
aop:是面向切面对象编程,是对oop面向对象编程的一种补充。
在项目编写之初,oop把核心业务进行实现,比如付款业务。在核心业务完成以后,又提出要添加辅助性的功能,比如在付款业务中加入日志记录,记录每个付款接口的执行时间。把这些辅助型的业务对象称为切面。
切面aspect:即提取出来的辅助业务对象(日志,执行事件,事务),TimeInfo对象
通知advice:即切面类中的方法(前置通知,返回通知,异常通知,最终通知,环绕通知)。begin和end方法
织入weave:在程序运行期间,通过动态代理模式,把一个切面对象中的某个方法作为通知 插入 到某个目标对象的某个连接点方法中。
连接点joinpoint:即目标对象中的需要被扩展的业务方法。pay方法
目标对象target:即被代理的原生对象。WechatPay对象
切入点pointcut:通过一个表达式来描述出来连接点的集合.
3.spring的aop使用
spring对aop进行了实现,基于ioc容器,spring持有了对象的引用,在对象上加代理对象,把切面中的通知方法织入到原对象中。
3.0 环境准备
1.添加6个aop需要使用的jar包
spring-aop;
spring-aspect;
cglib:动态代理模式第三方组件包,与Proxy类职责是一样的,但比Proxy更强大。Proxy方法创建代理类要求被代理对象必须有接口。cglib支持被代理对象没有上级接口。
aspectj组件3个jar包:这个组件是一个aop组件包,spring借用了该注解定义的注解。
3.1基于注解的aop实现
1.在xml文件中开启aop注解的识别
<!--用来识别aop相关的注解:@Aspect,@Pointcut,@Before,@After,@AfterReturning,@AfterThrowsing,@Around-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Aspect:定义到切面类上,表示当前类是一个切面对象
@Before:定义到切面类的方法上,表示该方法是一个前置通知方法。
@AfterReturning:定义到切面类的方法上,表示该方法是一个返回通知方法.
@AfterThrowing:定义到切面类的方法上,表示该方法是一个异常通知方法
@After:定义到切面类的方法上,表示该方法是一个最终通知方法
@Around:定义到切面类的方法上,表示该方法是一个环绕通知方法,经典玩法就是事务切面
2.定义一个切面类:
@Component
@Aspect
public class 类名(){}
3.在切面类定义切入点表达式:
方法1:通过execution(返回值 方法名(形参列表))
@Pointcut("execution()")
public void dd(){}
方法2:通过@annotation(注解名)
@Pointcut("@annotation(注解的全名)")
public void dd(){}
4.在切面类中定义通知方法
@Before,@After,@AfterReturning,@AfterThrowsing,@Around
在所有通知方法中都通过添加JoinPoint形参来获取连接点信息。
在返回通知中可以获取连接点方法返回值
在异常通知中可以获取连接点方法产生的异常
3.2基于xml配置的aop实现
<!--注册bean到spring容器-->
<bean id="logAspect" class="com.javasm.sys.aspect.LogAspect"></bean>
<!--进行aop的配置-->
<aop:config>
<aop:aspect ref="logAspect">
<aop:pointcut id="servicePointcut" expression="execution(* com.javasm.*.service.*.*(..))"></aop:pointcut>
<!--<aop:around method="aroundMethod" pointcut-ref="servicePointcut"></aop:around>-->
<!--<aop:after-returning method="afterReturning" pointcut-ref="servicePointcut" returning="obj"></aop:after-returning>-->
</aop:aspect>
</aop:config>