Spring
spring是什么
ApplicationContext.xml中基本标签的介绍
在该配置文件中我们常用的标签为beans,bean,import,alias和description标签。
1、其中beans主要用于包含多个bean。
2、bean标签是其核心,它用于注入对象它的属性主要有如下几个:
<bean id="name01" class="com.itlaobing.pojo.User" name="user3,user4" autowire="byType" scope="singleton">
<constructor-arg index="0" value="guigu"></constructor-arg>
</bean>
id:对象的名称,IOC容器中的id就相当于声明对象的对象名称一样不可以重复
class:对象所属的类型(必须为完全限定名)
scope:用于指定对象的作用域其值为Simpleton(表示单例模式,整个容器共享一个bean),ProtoType(表示原型模式每次调用容器中的一个对象就创建一个新的对象),还有三个作用域就与jsp的四大作用域有所相同分别为request,session
name:用于给对象取别名可以为多个,也不可以和id重复
autowire:这个属性用于表示bean是否支持自动装配,其值可以为主要为byName,byType表示通过在类定义中字段的名称和xml中bean的名称来实行自动装配,这个时候两个名称如果不匹配,自动装配会失败。第二个是用过byType来实现自动装配,也就是所通过xml中bean的class属性和我们所创建的类中字段的类型作为匹配的依据,这个时候的缺项是如果我们的xml中声明多个bean为同一个类型自动装配就会失败。
3、import标签主要是用于协作开发,用于导入多个xml配置文件
<import resource="beans.xml"></import>
其属性只有一个resource其值为所需要导入的文件的路径
4、alias标签主要是为对象取别名用的
<alias name="user" alias="user2"></alias>
其中name为原名,alias为别名
spring对象的注入
spring创建对象的基本流程
介绍srring的基本流程之前,需要知道spring中的核心配置文件applicationContext.xml,其用于构建一个spring容器(存储着一些对 象,容器中定义的对象一起创建一起使用。)
spring创建对象的基本流程包括
①在容器中注入一个对象
<bean class="com.itlaobing.pojo.User" id="user">
<property name="name" value="xie"></property>
</bean>
②通过xml文件创建一个容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
③通过容器获取对象
User user = (User) context.getBean("user");
user.show();
对象的注入本质
spring总对象的注入有两种方式有两种一种是通过构造方法注入对象还有有一种是通过setter方法注入。无论是通过构造方法注入还是通过setter方法注入一个对象,都是在xml配置文件中完成注入操作。然后使用xml配置文件来加载一个容器,再从容器中获取构建的对象。
spring构造方法通过构造方法注入
通过构造方法注入对象的前提是必须存在相对应的构造方法,通过相对应的构造方法注入的方式总共有三种分别为通过构造方法参数的索引注入,通过构造方法参数的类型注入,通过构造方法参数的名称代码如下:
<bean id="name01" class="com.itlaobing.pojo.User">
<constructor-arg index="0" value="guigu"></constructor-arg>
</bean>
<bean id="name02" class="com.itlaobing.pojo.User">
<constructor-arg name="name" value="guigu"></constructor-arg>
</bean>
<bean id="nama03" class="com.itlaobing.pojo.User">
<constructor-arg type="java.lang.String" value="guigu"></constructor-arg>
</bean>
需要注意的是constructor-arg中一共有五个可选的参数,其中index,name,type是用于锁定需要注入的参数,而value和ref用用于确定注入的值,value表示注入的是一个基本数据类型或者String,而ref则用于指向另外一个bean(该bean必须在xml中注册),ref的值为该bean的id值。
spring通过setter方法注入
通过setter方法注入显得更为简单,其只有一个property标签,name表示字段名,value表示字段的值
<bean class="com.itlaobing.pojo.User" id="user">
<property name="name" value="xie"></property>
</bean>
通过setter方法注入我们可以注入很多的类型,比如说基本数据类型,对象,空对象,对象数组,set集合,map映射,list,propertie配置等等,其注入形式都是通过一个大的标签比如list,array,set中包含property标签输入信息。比较特殊的有map和properties,map中使用entry注入,props中的key标志键该注入的值
<bean id="stu" class="com.itlaobing.pojo.Student"> <property name="name" value="xie"></property> <property name="books"> <array> <value>java基础</value> <value>java面向对象</value> <value>spring</value> <value>mynatis</value> </array> </property> <property name="card"> <map> <entry key="1" value="xie"></entry> <entry key="2" value="杨"></entry> </map> </property> <property name="games"> <set> <value>lol</value> <value>bob</value> <value>coc</value> </set> </property> <property name="hobby"> <list> <value>game</value> <value>music</value> <value>sport</value> </list> </property> <property name="info"> <props> <prop key="q">20190525</prop> <prop key="n">2019</prop> </props> </property> <property name="wife" value=""></property></bean>
p命名空间c命名空间
在使用两个命名空间之前需要在使用xmlns属性声明命名空间
c命名空间的声明
xmlns:c="http://www.springframework.org/schema/c"
p命名空间的声明
xmlns:c="http://www.springframework.org/schema/c"
使用命名空间可以将对象的注入注入写到bean标签的属性中
<bean id= "people" class = "com.itlaobing.pojo.People" c:name1="xie">
<bean id= "people" class = "com.itlaobing.pojo.People" p:name="da" >
自动装配
1、自动装配是什么:
自动装配就是让xml自动地配置根据java中成员变量的名称或者类型自动地给bean装配上对应的成员你变量的值
2、如何实现自动装配:
注意:使用注解开发需要在xml中开启注解支持,否则注解不会生效
<context:annotation-config></context:annotation-config>
a、根据bean标签的autowire属性选定自动装配的方式来实现自动装配
<bean id= "people" class = "com.itlaobing.pojo.People" autowired=“byType”>
b、使用注解实现自动装配
@Autowired@Qualifire("dog1")Dog dog;@AutowiredCat cat;
使用注解实现自动装配的方式是在需要自动装配的字段上或者setter方法上加入@Autowired注解来实现的,这个注解可以配合@Qualifire(“指定装配的名称”),来实现指定id的bean来装配属性。
c、通过java注解实现自动装配
public class User { //如果允许对象为null,设置required = false,默认为true @Resource(name = "cat2") private Cat cat; @Resource private Dog dog; private String str;}
使用@Resource实现自动装配也可以直接指定所要指定的名称。
第二种方式和第三种方式实行自动装配的区别:第二种方式实现自动装配默认使用byType自动装配如果失败就会报错而使用第三种方式实现自动装配会先使用byName方式自动装配如果失败则接着使用byType方式自动装配。
注入对象注解开发
其本质与ApplicationContext相同
注解支持(注意):
<context:annotation-config></context:annotation-config>
注入对象的注解开发的另一种方式是使用一个类来代替xml来实现bean标签的使用
@Configurationpublic class config { @Bean public Student getStudent(){ return new Student(); }}
@Configuration注解的作用时将该类配置为一个注解配置类,默认使用单例模式
@Bean则用于代替bean标签生成一个对象
我们在获取对应的对象时就需要使用如下代码
ApplicationContext con = new AnnotationConfigApplicationContext(config.class); Student student = con.getBean("getStudent",Student.class); System.out.println(student.getName());
这样也可以实现使用IOC的理念创建一个对象
介绍bean的注解开发
bean的注解开发本意使用注解代替bean中的配置
我们所需要了解的是注解开发的注解和衍生注解,自动装配作用域问题等
1、注解和衍生注解
@Component作用在一个类上相当于声明一个组件其作用相当于配置一个bean,配合value可以将给对象赋值
@org.springframework.stereotype.@Componentpublic class Controller { @Value("xie") private String name; public void setName(String name) { this.name = name; } public String getName() { return name; }}
对于三层架构中的控制层,业务层和数据层数据访问层中也提供了不同的@Component注解,其只是名字不同本质都是将其声明为一个Bean也可以配合@Value赋值使用
如果要使用注解开发Bean还需要在xml中配置一些值
<context:component-scan base-package="com.itlaobing"></context:component-scan>
AOP:面向切面编程
AOP的基本概念
aop即面向切面编程,是在程序编译和运行期间运用动态代理实现实现程序的统一维护的一种技术。它可以在不改变源程序的情况下给源程序加上一些功能,比如说日志,事务 ,安全,缓存等,降低程序的耦合性。
底层实现使用代理模式(重点)
静态代理(例子:租房):
静态代理指的是代理模式:利用一个例子来说明代理模式,我们租房子通常不直接找房东租房子,而是通过中介(代理)来租房子,这时代理和房东都有一个共同的方法为租房,房东只需要房子是否出租,而代理则负责出租的一切事务。在程序中表现为房东和代理有共同的行为,使用一个接口来定义他们的共同行为,房东类和代理类实心该接口。
优点:降低耦合性,房东类和代理类各司其职,房东类还可以多加功能
缺点:每次设计一个类都需要设计其代理类代码加倍,使用动态代理可以
动态代理(主要了解如何通过proxy中的方法动态生成一个代理):
动态代理很好地解决了静态代理中代码翻倍的缺点,它通过Proxy类中的newProxyInstance()方法来动态创建一个代理实例,并童年过InvocationHandler的invok方法来实现代理类中的方法,我们也可以通过代理程序加入我们所需要的方法比如说有关于日志,事务,缓存,和安全相关的方法。
如下是动态创建代理的程序:
package com.itlaobing.utils;import com.itlaobing.pojo.Host;import com.itlaobing.pojo.Rent;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyHandler implements InvocationHandler{ //动态生成的代理类所代理的接口是哪个 Rent rent; public ProxyHandler(Rent rent) { this.rent = rent; } public ProxyHandler() { } //通过接口创建动态创建代理对象 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } //代理对象的处理程序 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); Object result = method.invoke(rent,args); return result; } //附加的方法 public void seeHouse(){ System.out.println("看房子"); }}
如下是调用程序
import com.itlaobing.pojo.Host;import com.itlaobing.pojo.Rent;import com.itlaobing.utils.ProxyHandler;public class Client { public static void main(String[] args) { Host host = new Host(); ProxyHandler handler = new ProxyHandler(host); Rent proxy = (Rent) handler.getProxy(); proxy.rent(); }}
其中我们需要了解两个类:
一个是Proxy类:关注其中的一个方法
-
-
static Object
newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
-
它用于创建一个动态代理对象,第一个参数是一个类加载器,第二个参数是一个需要处理的接口,第三个参数是接口中方法的处理程序
另外一个需要了解的接口就是InvocationHandler其中只有一个方法invok用于调用所需要调用接口中的方法
-
方法摘要
Modifier and Type Method and Description Object
invoke(Object proxy, 方法 method, Object[] args)
处理代理实例上的方法调用并返回结果。如上程序一样我们可以在几口方法前面加上一些我们直接需要定义的方法
spring中实现AOP的三种方式
实现AOP有三种方式,第一种是通过实现接口来实现,第二种通过xml配置实现,第三种通过注解实现
①接口实现:
前置日志
package com.itlaobing.log;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class Before implements MethodBeforeAdvice { /** * method:方法 * objects:参数 * o:对象 * */ public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("实现了"+o.getClass().getName()+"类的"+method.getName()); }}
后置日志
package com.itlaobing.log;import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class After implements AfterReturningAdvice { public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("实现了"+o1.getClass().getName()+"类的"+method.getName()); }}
通过接口实现AOP需要业务逻辑类,前置日志类和一个后置日志类然后在xml中分别注入三个类的对象,前置和后置日志类分别实现MethodBeforeAdvice接口和AfterReturningAdvice接口然后使用xml配置这两个接口中的方法分别是业务逻辑的前置和后置。
<!-- <aop:config>--><!-- <aop:pointcut id="pointcut1" expression="execution(* com.itlaobing.service.UserService.*(..))"/>--><!-- <aop:advisor advice-ref="after" pointcut-ref="pointcut1"></aop:advisor>--><!-- <aop:advisor advice-ref="before" pointcut-ref="pointcut1"></aop:advisor>--> <!-- </aop:config>-->
这里有一个注意点就是配置切入点(pointcut),其中有一个表达式
表达式牵头的代表的是返回值的类型后面跟的是对哪个类的哪个方法生效表示所有方法或者所有类(…)表示所有参数类型的方法
②第二种实现的方式是通过切面配置:
它需要一个定制类和一个业务逻辑,定制类需要一个ProceedingJoinPoint类的对象作为参数,这个对象代表的是方法对象。
public class DiyAdvise { public void arround(ProceedingJoinPoint pj) throws Throwable { System.out.println("方法执行前"); //代表方法执行 pj.proceed(); //表示方法的描述信息 System.out.println(pj.getSignature()); System.out.println("方法执行后"); }}
然后需要在xml中配置一个切面信息
<aop:config> <aop:aspect id="diy"> <aop:pointcut id="pointcut" expression="execution(* com.itlaobing.service.UserServiceImpl.*(..))"/> </aop:aspect></aop:config>
需要配置的信息为切面的bean的id还有就是一个切入点
③第三种是使用注解配置切面信息
在第二种方式的基础上将xml配置切面信息用注解移植到java业务逻辑中,但是在xml中任然需要注册一个bean,注解仅仅是代替了切面的配置信息
package com.itlaobing.diy;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class DiyAdvise { @Around("execution(* com.itlaobing.service.UserService.*(..))") public void arround(ProceedingJoinPoint pj) throws Throwable { System.out.println("方法执行前"); pj.proceed(); System.out.println(pj.getSignature()); System.out.println("方法执行后"); }}
spring中使用AOP实现事务
事务是什么
事务的概念:事务通俗的理解为业务中的一系列非原子操作,这些操作要么全做要么不做,就像我们取银行取钱一样,要么钱取出来,账目上的数字也减少了,要么两个操作都不做
事务的四大特性:
原子型:要么全做要么全不做
一致性:指的是业务处理前的数据一致处理后的数据也一直
隔离性:数据库为多个用户开放权限,多个用户一起访问数据库,多个事务并发期间互相隔离
持久性:事务完成后对数据的改变是不变的
事务的作用:事务用于保护数据库中的数据保持一致性
AOP实现事务
AOP实现事务首先需要一个事务管理器
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="username" value="root"></property> <property name="password" value="123"></property> <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC"></property> </bean> <bean id="datasourcetransactionmanager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg name="dataSource" ref="datasource"></constructor-arg> </bean><!-- 这些代码相当于我们主动创建aop的日志处理类--> <tx:advice id="tx" transaction-manager="datasourcetransactionmanager"> <tx:attributes> <tx:method name="update"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="pointcut" expression="execution(* com.itlaobing.service.UserServiceImpl.*(..))"/> <aop:advisor advice-ref="tx" pointcut-ref="pointcut"></aop:advisor> </aop:config>
IOC(控制反转)
IOC的基本原理:
①原先我们设计程序的弊端:原先我们创建的对象由用户需求的变化而去改变源代码。这会使得程序源码改变的代价十分昂贵。
②使用IOC之后,程序给用户一个接口让用户取创建对象,程序员只要把精力放在业务的实现上。
③主动权改变,不使用IOC的程序主动权在业务层,而使用了IOC的程序主动权在用户的身上。
④DI(set注入)是IOC的其中一种实现方式,还可以通过xml和注解实现IOC
整合mybatis
整合方式一:
第一步:在spring中整合mybatis需要创建三个bean,一个是数据来源,一个是SqlsessionFactory的bean,还有一个是sqlSession的bean
第一个bean的创建依赖于DriverManagerDataSource
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="password" value="123"></property> <property name="username" value="root"></property> <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC"></property> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property></bean>
第二个bean为SqlSessionFactory,我们知道在mybatis中它的创建基于mybatis-config.xml我们可以使用。而在整合时我们依赖SqlSessionFactoryBean这个类创建它,它相当于mybatis-config.xml,在mybatis-config.xml能配置的东西,我们通过注入这个类的对象也可以做到,甚至可以直接宝行mybatis-config.xml这个文件
<bean id="sqlsessiomfactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="mybatis-config.xml"></property> <property name="dataSource" ref="dataSource"></property> <property name="mapperLocations" value="classpath:com/itlaobing/dao/*.xml"></property></bean>
第三个需要创建的bean是SqlSession,我们依赖于SqlSessionTemplate这个类来创建它,它只需要注入一个SqlSessionFactoryBeanbean就可以创建。
②完成了上面三个bean的注入之后我们就相当于完成了mybatis中sqlsession的创建我们可以调用sqlsession创建mapper来完成mybatis中的一些操作,但既然使用了Spring我们就将mapper也当做一个bean来注入(比起mybatis,要在spring中注入一个mapper类,mapper必须创建一个类而不是使用xml),这个类的创建和注入依赖于SqlSessionTeamplate类所以要在其中创建一个sqlsessionteamplate类用于方面mapper的注入。
③完成mapper类的创建之后我们就可以直接在容器中获取mapper进行数据库相关操作了。
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{ private SqlSessionTemplate sqlsession; public void setSqlsession(SqlSessionTemplate sqlsession) { this.sqlsession = sqlsession; } public List<User> Select(){ UserMapper mapper = sqlsession.getMapper(UserMapper.class); return mapper.select(); } @Override public List<User> select() { return null; }}
第二种整合方式
这种整合方式前面的步骤与上面相同,只不过在省去了创建sqlsession这一步,改为继承SqlSessionDaoSupport这个类,类中包含了对sqlsession的创建
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{ @Override public List<User> select() { return getSqlSession().getMapper().select(); }}
这种方式在类文件中省去了mapper类宝行SqlSessionTeamplate类和在xml中创建SqlSession这一步sqlsession因此需要将SqlSessionFactoryBean直接注入到mapper中