Spring是一个轻量级框架,Spring致力于Java EE应用的各种解决方案,贯穿表现层,业务层和持久层。并且Spring以高度的开放性与其他原有的框架进行无缝整合。
Spring的整体架构:
Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。
在Spring配置文件中,使用
<bean id="" class=""/>
通常,Spring为Bean属性赋值时通过调用属性的setter方法实现的。
接着,我们可以通过ClassPathXmlApplicationContext实例化Spring的上下文。
public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); User user = context.getBean("user", User.class); System.out.println(user.getName()); }
ApplicationContext是一个接口,负责读取Spring配置文件,管理对象的加载、生成、维护Bean对象之间的依赖关系,负责Bean的生命周期等。ClassPathXmlApplicationContext是ApplicationContext接口的实现类,用于从classPath路劲中读取Spring配置文件。
实体类创建有参构造函数:
public User(String name) { this.name = name; }
application.xml
<!-- 构造器注入 --> <bean id="userCust" class="com.vxzx.pojo.User"> <constructor-arg name="name" value="广州"/> </bean>
实体类:
private String name; private Address address; private String[] hobbis; private List<String> likes; private Map<String,String> cid; private Set<String> setcontext; private Properties por;
application.xml
<!-- 2、复杂类型注入 --> <bean id="address" class="com.vxzx.pojo.Address"> <property name="address" value="广东"/> </bean> <!-- 1、String注入 --> <bean id="user1" class="com.vxzx.pojo.User"> <property name="name" value="vxzx"/> <!-- 2、复杂类型注入,需要使用ref --> <property name="address" ref="address"/> </bean> <!-- 3、array[]数组注入 --> <bean id="user2" class="com.vxzx.pojo.User"> <property name="hobbis"> <array> <value>打球</value> <value>听歌</value> </array> </property> </bean> <!-- 4、List<>注入 --> <bean id="user3" class="com.vxzx.pojo.User"> <property name="likes"> <list> <value>list1</value> <value>list2</value> <value>list3</value> </list> </property> </bean> <!-- 5、Map<>注入 --> <bean id="user4" class="com.vxzx.pojo.User"> <property name="cid"> <map> <entry key="1" value="张三"/> <entry key="2" value="李四"/> <entry key="3" value="王五"/> </map> </property> </bean> <!-- 6、set注入 --> <bean id="user5" class="com.vxzx.pojo.User"> <property name="setcontext"> <set> <value>set1</value> <value>set2</value> <value>set3</value> </set> </property> </bean> <!-- 7、properties注入 --> <bean id="user6" class="com.vxzx.pojo.User"> <property name="por"> <props> <prop key="username">vxzx</prop> <prop key="password">123456</prop> </props> </property> </bean>
扩展方式注入一共有两种:p-namespace和c-namespace
使用该扩展首先要先导入:
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
p-namespace的具体使用:p-namespace 允许您使用bean
元素的属性(而不是嵌套的<property/>
元素)来描述协作 Bean 的属性值,或同时使用这两者。
<!-- 扩展c:namespace和p:namespace --> <bean id="user8" class="com.vxzx.pojo.User" p:name="lin" p:age="11"/>
c-namespace的具体使用:c:
命名空间执行,基于构造函数的依赖注入
<bean id="user9" class="com.vxzx.pojo.User" c:name="lzx"/>
创建 bean 定义时,将创建一个配方来创建该 bean 定义所定义的类的实际实例。默认状态下是单例模式。
Scope | Description |
---|---|
singleton | (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。 |
prototype | 将单个 bean 定义的作用域限定为任意数量的对象实例。 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext 中有效。 |
session | 将单个 bean 定义的范围限定为 HTTP Session 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
application | 将单个 bean 定义的范围限定为ServletContext 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
websocket | 将单个 bean 定义的范围限定为WebSocket 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
仅 Management 一个 singleton bean 的一个共享实例,并且所有对具有 ID 或与该 bean 定义相匹配的 ID 的 bean 的请求都会导致该特定的 bean 实例由 Spring 容器返回。
<bean id="user" class="com.vxzx.pojo.User" scope="singleton"/>
User user = context.getBean("user", User.class); User user2 = context.getBean("user", User.class); System.out.println(user==user2); //输出为true
每次对特定 bean 提出请求时,bean 部署的非单一原型范围都会导致创建一个新 bean 实例。
<bean id="user" class="com.vxzx.pojo.User" scope="prototype"/>
User user = context.getBean("user", User.class); User user2 = context.getBean("user", User.class); System.out.println(user==user2); //输出为false
Spring 会在上下文自动寻找,并自动给bean装配属性。
一般来说有三种装配方式:
<bean id="user" class="com.vxzx.pojo.User"> <property name="name" value="lin"/> </bean>
autowire="byName":寻找和自己对象set方法后面的值对应的beanid
<bean id="cat" class="com.vxzx.pojo.Cat"/> <bean id="user" class="com.vxzx.pojo.User" autowire="byName"/>
autowire="byType":寻找和自己对象属性相同的bean(class)
<bean id="cat" class="com.vxzx.pojo.Cat"/> <bean id="user" class="com.vxzx.pojo.User" autowire="byType"/>
总结:
byName:保证bean的id唯一,bean需要和注入的属性的set的值一致。
byType:保证bean的class唯一,需要和注入的属性的类型一致。
与往常一样,您可以将它们注册为单独的 bean 定义,但是需要记得加上<context:annotation-config/>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean id="user" class="com.vxzx.pojo.User"/> <bean id="fish" class="com.vxzx.pojo.Fish"/> </beans>
<context:annotation-config/>
仅在定义它的相同应用程序上下文中查找 bean 上的注解。
将@Autowired
注解 应用于构造函数或者setter方法。
//方式一: @Autowired private Fish fish; //方式二: @Autowired public void setFish(Fish fish) { this.fish = fish; }
使用Autowired还可以自定义bean对应的id 的值。使用@Qualifier(value = "")
@Autowired @Qualifier(value = "yu") private Fish fish;
<bean id="yu" class="com.vxzx.pojo.Fish"/>
使用@Autowired
注解的时候,该对应的bean的值必须的存在的,不能为空,不然会报错。当然,如果想解决这种问题,我们可以使用required
。
required
的值为true,则表示需要该bean的值不为空。required
的值为false,则表示允许该bean的值为空。@Autowired(required = false) private Bird bird;
使用@Nullable
,说明这个字段可以为null;
使用@Resource
java注解实现自动装配:该注解为java的注解,而不是spring 的注解。
@Resource private Mouse mouse;
<bean id="mouse" class="com.vxzx.pojo.Mouse"/>
@Resource
的使用与@Autowired
基本差不多,均可标注在字段或属性的setter方法上,但还是有一些区别的。
@Resource
跟@Autowired
的区别:
@Resource
是spring中提供的注解,@Autowired
是java中的提供的注解。
@Autowired
默认使用byType方式自动装配,如果byType方式识别不了,则会使用byName方式。
简单来说就是:先byType,再byName
@Resource
默认使用byName方式自动装配,如果byName方式识别不了,则会使用byType方式。简单来说就是:先byName,再byType
在Spring中,如果想要使用注解开发,必须先导入AOP包,不然无法使用。
开启注解扫描包:
<!-- 开启注解扫描包: --> <context:component-scan base-package="com.vxzx.pojo"/> <context:annotation-config/>
@Compoent
:放在类上,说明这个类被Spring管理了,相当于bean。
<bean id="" class=""></bean>
@Component public class User { private String name; }
@Value
:放在属性上,可以给该属性赋值,相当于bean中的property。
<property name="" value=""/>
@Value("vx") private String name;
@Scope
:放在类上,可以给这个类设置bean 的作用域。相当于bean中的scope。
<bean scope="singleton"/>
@Scope("singleton") public class User { }
@Compoent
的几个衍生注解:
其本质都与@Component一样,只是不同包下的注解名字不同。
Spring提出的强大的JavaConfig这种类型安全的Bean装配方式,它基于Java代码的灵活性,使得装配的过程也变得及其灵活。即使用java的方式配置spring。使用JavaConfig方式则不需要使用XML配置。
@Configuration
@Configuration public class Myconfig { public User getuser(){ return new User(); } }
以上配置等同于之前配置的一个application.xml文件。
还可以加上@ComponentScan
组件扫描;
@Configuration @ComponentScan(basePackages = "com.vxzx.pojo") public class Myconfig { @Bean public User getuser(){ return new User(); } }
表示该配置类,扫描了pojo包下的所有类。
@Bean
使用@Bean注解我们可以实现XML配置中手动配置第三方Bean的功能,这里我们使用方法来定义Bean,并在方法前面加注@Bean注解,表示要将该方法返回的对象加载到Spring容器中。
@Bean
是方法级 注解,是 XML <bean/>
元素的直接类似物。注解 支持<bean/>
提供的某些属性。
@Configuration public class Myconfig { @Bean public User getUser(){ return new User(); } }
加上@bean相当于:
<bean id="getUser" class="com.acme.services.User"/>
@Import
使用@import注解可以将其他的类,注解导入到该配置类中来,整合成一个类。@Import
注解 允许从另一个配置类中加载@Bean
定义
@Configuration @ComponentScan(basePackages = "com.vxzx.pojo") @Import(People.class) public class Myconfig { @Bean public User getuser(){ return new User(); } }
即上文中将另外一个类People注入到该配置类中,与User类整合在一起。
与实例化ClassPathXmlApplicationContext
时将 Spring XML 文件用作 Importing 的方式几乎相同,实例化AnnotationConfigApplicationContext
时可以将@Configuration
类用作 Importing。
如果使用@Configuration
类作为 Importing ,则可以使用 AnnotationConfigApplicationContext
实例化 Spring 容器;
当提供@Configuration
类作为 Importing 时,@Configuration
类本身被注册为 bean 定义,并且该类中所有已声明的@Bean
方法也被注册为 bean 定义。
public void test01(){ ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class); User getuser = (User) context.getBean("getuser"); System.out.println(getuser.getName()); }
代理模式:为其他对象提供一种代理以控制这个对象的访问。
静态代理是由程序或特定工具自动生成的源代码,在程序运行时,.class
文件就已经存在了。
实体类:
public interface Renti { void rent(); }
public class Rent implements Renti{ public void rent() { System.out.println("buy house!----真实对象"); } }
代理类:
public class RentProxy implements Renti{ private Rent rent; public RentProxy(Rent rent) { this.rent = rent; } public void rent() { System.out.println("fiex!-----代理对象"); rent.rent(); } }
测试类:
//静态代理模式: @Test public static void main(String[] args) { Rent rent = new Rent(); RentProxy rentproxy = new RentProxy(rent); rentproxy.rent(); }
动态代理指在运行时自动生成的代理类。
动态代理需要实现InvocationHandler接口的invoke方法:
创建方法,必须调用Proxy类的静态方法newProxyInstance,该方法如下:
Object Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException
最后,在重载的invoke方法中,使用method.invoke()方法传参
实体类:
public interface Rent { void rent(); }
public class RentRel implements Rent{ public void rent() { System.out.println("buy house!---真实主体!"); } }
代理类:
public class Proxydy implements InvocationHandler { //被代理的接口。 private Object object = null; //生成得到的代理类。 public Object newReal(Object real){ this.object = real; Class<?> realClass = real.getClass(); Object proxyInstance = Proxy.newProxyInstance(realClass.getClassLoader(),realClass.getInterfaces(),this); return proxyInstance; } //处理代理实例,并返回结果。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("进入代理!!!!"); //本质:使用反射机制实现动态代理。 Object invoke = method.invoke(object, args); return invoke; } }
测设类:
//动态代理模式: public static void main(String[] args) { com.vxzx.dynamicproxy.Rent real = (com.vxzx.dynamicproxy.Rent) new Proxydy().newReal(new RentRel()); real.rent(); }
AOP英文名为Aspect Oriented Programming,意为面向切面编程,通过预编译方式和运行期间动态代理实现程序功能统一维护的一种技术。可以在其 前后加入 需要添加的东西。即在不改变代码的情况下,增加新的功能。
AOP中名词的解释:
目标对象类:
public interface UserServer { public void add(); }
public class UserServerImp implements UserServer { public void add() { System.out.println("添加用户!"); } }
增强类:before
public class Before implements MethodBeforeAdvice { //method:要执行的目标对象的方法 //objects(args):参数 //o(target):目标对象 public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("执行了" + o.getClass() + "的" + method.getName() + "方法"); } }
增强类:before
public class After implements AfterReturningAdvice { //o(returnValue):返回值; //其他的与before一样。 public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了" + o1.getClass() + "的" + method.getName() + "方法" + "返回值:" + o); } }
配置类:
<!-- 注册bean --> <bean id="user" class="com.vxzx.server.UserServerImp"/> <bean id="before" class="com.vxzx.server.Before"/> <bean id="after" class="com.vxzx.server.After"/> <!-- 配置AOP --> <aop:config> <!-- 切入点; execution表达式:execution(修饰词 类名.方法名.参数 ) --> <aop:pointcut id="point" expression="execution(* com.vxzx.server.UserServerImp.*(..))"/> <!-- 执行环绕增强: --> <!-- 把before类切入到point中去 --> <aop:advisor advice-ref="before" pointcut-ref="point"/> <aop:advisor advice-ref="after" pointcut-ref="point"/> </aop:config>
测试类:
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserServer user = (UserServer) context.getBean("user"); user.delete();
自定义增强方法:
public class Aopasp { public void b(){ System.out.println("前置增强"); } public void a(){ System.out.println("后置增强"); } }
配置类:
<bean id="asp" class="com.vxzx.server.Aopasp"/> <aop:config> <!--切入点:--> <aop:pointcut id="pointcut" expression="execution(* com.vxzx.server.UserServerImp.*(..))"/> <!--使用切面aspect方法: ref 需要引用的类 --> <aop:aspect ref="asp"> <!--前置增强:--> <aop:before method="b" pointcut-ref="pointcut"/> <!--后置增强: --> <aop:after method="a" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
测试类:
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserServer user = (UserServer) context.getBean("user"); user.delete();
注解实现类:
//表示该类是一个通知类(切面): @Aspect public class proxyanno { //设置切入点: @Pointcut("execution(* com.vxzx.serveranno.UserServerImp.*(..))") public void pc(){} @Before("proxyanno.pc()") // @Before("execution(* com.vxzx.serveranno.UserServerImp.*(..))") 指定前置增强,并且指定切入点 public void before(){ System.out.println("注解---前置增强"); } @After("proxyanno.pc()") // @After("execution(* com.vxzx.serveranno.UserServerImp.*(..))") 指定后置增强,并且指定切入点 public void after(){ System.out.println("注解---后置增强"); } @Around("proxyanno.pc()") public void around(ProceedingJoinPoint pj) throws Throwable { System.out.println("环绕增强--前"); pj.proceed(); System.out.println("环绕增强--后"); } }
配置类:
<bean id="proxyanno" class="com.vxzx.serveranno.proxyanno"/> <!-- 开启注解实现AOP: --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
测试类:
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserServer user = context.getBean("user", UserServer.class); user.add();
需要导入的jar包:
整合Mybatis,数据源和sqlSessionFactory都可以在spring.xml中创建,即Mybatis中的所有配置都可以在Spring.xml中配置了,不在需要单独创建一个Mybatis.xml来创建数据源和sqlSessionFactory等信息。
配置一个为spring.xml的文件:
<bean id="DataSource" class="org.springframework.jdbc.datasource.DriverMangerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=ture&apm;characterEncoding=utf-8"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean>
<bean id="sqlSessionFactory" class="org.mybaits.spring.SqlSessionFactoryBean"> <property name="Datasource" ref="DataSource"></property> <!-- 绑定mybatis配置文件:--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/vxzx/mapper/*.xml"/> </bean>
<bean id="sqlsession" class="org.mybatis.spring.SqlSessionTemplate"> <constructot-arg index="0" ref="sqlSessionFactory"></constructot-arg> </bean>
<bean id="usermapperimp" class="com.vxzx.mapper.userMapperImp"> <property name="sqlSession" ref="sqlsession"></property> </bean>
实体类:需生成GET、SET方法
public class User { private int id; private String name; private String pwd; }
接口类:
public interface UserMapper { public List<User> selectu(); }
userMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.vxzx.mapper.UserMapper"> <select id="selectu" resultType="user"> select * from user; </select> </mapper>
具体实现类:UserMapperImp,使用到了SqlSessionTemplate;SqlSessionTemplate=sqlSession
public class UserMapperImp implements UserMapper { private SqlSessionTemplate sqlsession; public void setSqlsession(SqlSessionTemplate sqlsession) { this.sqlsession = sqlsession; } public List<User> selectu() { UserMapper mapper = sqlsession.getMapper(UserMapper.class); return mapper.selectu(); } }
测试类:
@Test public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); UserMapper mapper = context.getBean("usermapperimp",UserMapper.class); List<User> selectu = mapper.selectu(); for (User user:selectu){ System.out.println(user); } }
利用实现类继承SqlSessionDaoSupport,来自动生成sqlSessionTemplate。
实现类:
public class UserMapperImp2 extends SqlSessionDaoSupport implements UserMapper{ public List<User> selectu() { SqlSession session = getSqlSession(); UserMapper mapper = session.getMapper(UserMapper.class); return mapper.selectu(); } }
配置类XML:
<!-- 继承SqlSessionDaoSupport类,实现sqlSessionTemplate --> <bean id="userMapper2" class="com.vxzx.mapper.UserMapperImp2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean>
测试类:
@Test public void mbtest02(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml"); UserMapper mapper = context.getBean("userMapper2", UserMapper.class); for (User user : mapper.selectu()) { System.out.println(user); } }
总结:1、创建生成数据源
2、创建sqlSessionFactory
3、创建sqlSessionTemplate
4、创建具体实现类
5、将具体实现类注入到Spring中
Spring中propagation的七种事务配置:
事务属性 | 详解 |
---|---|
REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED | 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。 |
事务的重要性:
在Spring中配置事务:
<!-- 设置声明式事务: 需要创建一个 DataSourceTransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="DataSource"/> </bean> <!-- 结合AOP是实现事务的织入 --> <tx:advice id="traDriver" transaction-manager="transactionManager"> <!-- propagation :设置传播方式! --> <tx:attributes> <!-- <tx:method name="add" propagation="REQUIRED"/>--> <!-- <tx:method name="delete" propagation="REQUIRED"/>--> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置AOP切面,将事务切入到需要实现事务的实例中去: --> <aop:config> <aop:pointcut id="driverPoint" expression="execution(* com.vxzx.mapper.*.*(..))"/> <aop:advisor advice-ref="traDriver" pointcut-ref="driverPoint"/> </aop:config>
总结:如果遇到sql语句中有错误,但是却执行成功,则需要配置事务;