VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > Java教程 >
  • spring学习

spring相关知识#

spring相关概述#

  • 轻量级的Javaee开源框架,解决企业开发的复杂性
  • 核心特性ioc和aop

ioc 控制翻转,把创建对象交给spring管理
aop 不修改源代码就行功能增强

  • 特点
    方便接偶,简化开发
    aop编程支持
    方便与其他框架整合
    方便进行事务操作

入门案例#

  1. 创建maven项目
  2. 导入相关依赖
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.13.2</version>
	<scope>test</scope>
</dependency>
  1. 进行测试
	ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
	// 获取创建的对象
	User user = context.getBean("user", User.class);

概念和原理#

  1. ioc

控制翻转,把对象创建和对象之间的调用过程交给spring管理,降低耦合度,入门案例就是ioc的说过话i先

  1. 底层原理

xml解析,工厂模式,反射

原始通过new的方式来进行创建对象,当serveic中创建dao的时候,dao的路径发生了变化,service中也要进行路径的更改,可以通过工厂模式进行降低相互之间的依赖
image
此时工厂和dao之间产生了耦合,可以使用xml或者properties进行进一步解耦,spring中使用了xml的方式进行。
image
ioc的容器本质上就是对象工厂,spring在启动的时候解析xml配置文件,根据配置的规则创建出对象就行存放到工厂中,然后进行相应读取,默认是立即加载,也可以配置懒加载。
对象的两个接口beanFactoryapplicationContext,前者是spring内部使用的接口,后者是扩展类,提供了更强大的功能。前者是延迟加载,后者是立即加载。
image
常见的实现类为ClassPathXmlApplicationContext,FileSystemXmlApplicationContext,前者是类路径,后者是绝对路径。
3. bean管理的主要内容:
创建对象(xml配置方式/注解方式),注入属性。

基于xml创建对象

在spring配置文件中,使用bean标签,标签里面添加对应的属性,可以实现。
id属性表示唯一标识,classs属性为类全路径,后续spring根据改class的值通过反射默认午餐方式进行床架接你对象

基于xml方式注入属性
  • 通过set方式进行注入(常用)
    <property name="name" value="demo"/>
  • 通过有参构造
    <constructor-arg name="name" value="demo"></constructor-arg>
  • 通过p标签进行注入本质通过set方式进行注入,语法糖的效果
    需要导入命名空间
    <bean id="user" class="com.haha.spring.User" p:name="demo"/>
    注入null值
    <property name="name"><null/></property>
    注入特殊的符号,使用<![CDATA[]]>包含
    <bean id="user" class="com.haha.spring.User" >
        <property name="name">
            <value><![CDATA[<你好>]]></value>
        </property>
    </bean>
  • 属性的赋值
 <bean id="emp" class="com.haha.spring.bean.Emp">
        <property name="ename" value="demo"/>
        <property name="gender" value="mail"/>
        <!--通过set的方式-->
        <property name="dept" ref="dept"></property>
        <!--本质是先get出来dept的属性,不常用,emp中要有getDept的方法,然后再调用dept的setDname进行复制-->
        <property name="dept.dname" value="技术"/>
 </bean>
  • 注入数组集合map相关的属性
    <bean id="course01" class="com.haha.spring.bean.Course"/>

   <bean id="stu" class="com.haha.spring.bean.Stu">
       <property name="list">
           <list>
               <value>list1</value>
               <value>list2</value>
           </list>
       </property>
       <property name="maps">
           <map>
               <entry key="key1" value="value1"/>
               <entry key="key2" value="value2"/>
           </map>
       </property>
       <property name="sets">
           <set>
               <value>set1</value>
               <value>set2</value>
           </set>
       </property>
       <property name="courseList">
           <list>
               <!--内部复制-->
               <bean id="course" class="com.haha.spring.bean.Course">
                   <property name="cname" value="demo"/>
               </bean>
               <!--引用外面的对象-->
               <ref bean="course01"/>
           </list>
       </property>
   </bean>
  • 对象的创建(可以通过depondon来改变创建的顺序)
  1. 普通的bean对象
  2. bean工厂的对象(此时分为静态工厂和示例的工厂)
    普通的bean直接通过bean标签获取的类型和class中定义的类型相同。还有一种方式是bean需要实现beanFactory的接口,然后实现里面的方法,通过xml的方式进行注册。
  • bean的作用域
    默认情况下是单例的模式,可以通过scope的标签进行设置。可选的值为singletonprototype。后者是懒加载的方式,在获取的时候才会进行示例的创建。
  • bean的生命中周期
  1. 默认调用无参构造就行创建对象
  2. 调用set方法进行属性的注入
  3. 把bean的实例传递给后置处理器前置方法
  4. 执行初始化方法
  5. 把bean的周期传递给后置处理器的后置方法
  6. 获取创建bean的示例对象
  7. 在销毁的时候执行销毁的方法
public class Orders {

    //无参数构造
    public Orders() {
        System.out.println("第一步 执行无参数构造创建bean实例");
    }

    private String oname;
    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二步 调用set方法设置属性值");
    }

    //创建执行的初始化的方法
    public void initMethod() {
        System.out.println("第三步 执行初始化的方法");
    }

    //创建执行的销毁的方法
    public void destroyMethod() {
        System.out.println("第五步 执行销毁的方法");
    }
}
public class MyBeanPost implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}
 	// 加载spring配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo2.xml");
        // 获取创建的对象
        Orders orders = context.getBean( Orders.class);
        context.close();

<bean id="order" class="com.haha.spring.demo2.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
       <property name="oname" value="demo"></property>
   </bean>
    <!--配置后置处理器,会对所有创建的对象生效-->
    <bean  class="com.haha.spring.demo2.bean.MyBeanPost"/>

image

  • bean的自动装配
    可以根据名称或者属性的类型就行相应的自动的装配,为后续注解式的注入打下基础
    <bean id="dept" class="com.haha.spring.bean.Dept"/>
    <bean class="com.haha.spring.bean.Emp" id="emp" autowire="byType"/>
  • 操作外部属性的文件
	<!--引入命名空间-->
    <context:property-placeholder location="jdbc.properties"/>
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="name" value="${prop.userName}"/>
        <property name="password" value="${prop.password}"/>
        <property name="url" value="${prop.url}"/>
        <property name="driverClassName" value="${prop.driverClass}"/>
    </bean>
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
基于注解的方式创建对象

基于注解的方式可以简化xml的配置,需要有spring-aop包的支持

  1. 导入spring-aop包的支持
  2. 开启注解的扫描
  3. 在需要配置的累上添加相应的注解
    image
    默认的filter是扫描配置包下的所有类,里面有各种的扫描类型,可以进行配置,如果需要详细的信息,点击此处
  • 基于注解的方式实现属性的注入
    autowire按照类型进行匹配,如果改类型有多个示例,按照属性的名称进行相应的查找,如果查找不到跑出异常,后面可以在添加上qualifiter进行按照名称进行查询,是spring提供的注解,也可以使用resource进行相应的注入,这是Java规范提供的注解。默认根据名称进行注入,也可以指定bytype属性根据类型进行相应的注入。vaule注解可以注入普通类型的属性。
  • 完全注解的开发
@Configuration
@ComponentScan(basePackages = "com.haha.spring")
public class SpringConfig {
    // 配置相应的属性对象,类似于xml中的bean标签
}
    // 加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
context.getBean("xxx");

带泛型的依赖注入
image
image
在对象注入的时候可以根据不同泛型进行注入

aop的相关知识#

利用aop可以对业务逻辑的各个部分进行相应的隔离,从而达到各部分之间的耦合度降低,提高程序的可用性,提高开发的效率。不通过修改源码的方式,在主干的功能里面添加新的功能
image
aop底层使用动态代理的方式进行实现。

  1. 有接口的情况,创建接口的实现类对象,增强累的方法。
  2. 没有接口的情况,使用CGLIB进行相应的动态代理,通过继承该类的方式。
    jdk的动态代理
public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces = {UserDao.class};
        Object proxyInstance = Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName() + "");
                Object o = method.invoke(new UserDaoImpl(), args);
                return o;
            }
        });
        UserDao dao = (UserDao) proxyInstance;
        dao.add(1,2);

    }
}
  • aop相关术语
  1. 连接点 类中可以被增强的方法
  2. 切入点 类中真正增强的方法
  3. 通知 实际增强的逻辑部分称为通知
    image

通知类型
前置,后置,环绕,异常,最终通知

4. 切面:把通知应用到切入点的过程
切入点表达式
访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
访问修饰符可以省略

  • com.haha.demo....*(..)表示demo包以及子包下的所有类的所有方法。
    *..表示当前的包以及子包
    可以通过order注解进行改变切面的顺序,值越小优先级越高
    aop的注解配置
       <!--一般都是通过aspectj来实现aop的功能-->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-aspects</artifactId>
           <version>5.2.6.RELEASE</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-test</artifactId>
           <version>5.2.6.RELEASE</version>
           <scope>test</scope>
       </dependency>
	   <context:component-scan base-package="com.haha.spring"/>
       <!--开启基于注解的AOP功能-->
       <aop:aspectj-autoproxy/>
/**
 * @Before: 前置通知, 在方法执行之前执行
 * @After: 后置通知, 在方法执行之后执行
 * @AfterRunning:返回通知, 在方法成功执行返回结果之后执行
 * @AfterThrowing: 异常通知, 在方法抛出异常之后
 * @Around: 环绕通知, 围绕着方法执行
 */
@Aspect
@Component
public class LogUtil {

    @Pointcut("execution(* com.haha.spring.*.*(..))")
    public void myPoint(){};

    //定义通知方法
    @Before(value = "myPoint()")
    public void LogBefore(JoinPoint joinPoint) {
        System.out.println("LogBefore方法被执行了...,方法名称"+joinPoint.getSignature().getName());
    }
    @After(value = "myPoint()")
    public void LogAfter() {
        System.out.println("LogAfter方法被执行了...");
    }

    @AfterReturning(value = "myPoint()",returning = "end")
    public void LogAfterReturning(Object end) {
        System.out.println("LogAfterReturning方法被执行了..."+end);
    }

    @AfterThrowing(value = "myPoint()",throwing = "throwing")
    public void logThrowing(Exception throwing){
        throwing.printStackTrace();
    }

}
@SpringJUnitConfig(locations = "classpath:demo.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {
    @Autowired
    User user;

    @Test
    public void test01(){
        System.out.println(user.add(1, 2));
    }

}

环绕通知
本身就是一个动态代理。可以改变返回值

 @Around("myPoint()")
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("环绕方法的前置");
            Object o = joinPoint.proceed();
            System.out.println("环绕方法的后置");
            return o;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕方法的异常");
            return null;
        }finally {
            System.out.println("环绕方法的最终通知");
        }

    }

aop的xml配置

 <!--一般都是通过aspectj来实现aop的功能-->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-aspects</artifactId>
           <version>5.2.6.RELEASE</version>
       </dependency>
     <!--1.将目标类与切面类都加入到ioc容器中-->
  <bean id="userServlet" class="pers.lele.servlet.UserServlet"></bean>
  <bean id="logUtils" class="pers.lele.utils.LogUtils"></bean>
  <bean id="timeUtils" class="pers.lele.utils.TimeUtils"></bean>
  <!--2.告诉Spring哪个是切面类-->
  <aop:config>
      <!--切入点-->
      <aop:pointcut id="deleteUserById"
                    expression="execution(public * pers.lele.servlet.UserServlet.deleteUserById(Integer))"/>
      <!--2.告诉Spring哪些是切面类-->
      <aop:aspect ref="logUtils">
          <!--3.告诉Spring通知方法何地运行-->
          <aop:before method="LogBefore" pointcut-ref="deleteUserById"/>
          <aop:after-returning method="LogAfterReturning" pointcut-ref="deleteUserById" returning="result"/>
          <aop:after-throwing method="LogAfterThrowing" pointcut-ref="deleteUserById" throwing="e"/>
          <aop:after method="LogAfter" pointcut-ref="deleteUserById"/>
          <aop:around method="LogAround" pointcut-ref="deleteUserById"/>
      </aop:aspect>
      <aop:aspect ref="timeUtils">
          <aop:before method="LogBefore" pointcut-ref="deleteUserById"/>
          <aop:after-returning method="LogAfterReturning" pointcut-ref="deleteUserById" returning="result"/>
          <aop:after-throwing method="LogAfterThrowing" pointcut-ref="deleteUserById" throwing="e"/>
          <aop:after method="LogAfter" pointcut-ref="deleteUserById"/>
      </aop:aspect>
  </aop:config>

事务的相关知识#

      <!--1.配置事务管理器,让其进行事务控制-->
         <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
             <!--控制住数据源-->
             <property name="dataSource" ref="dataSource"></property>
         </bean>
  <!--2.开启基于注解的事务控制模式,依赖tx名称空间 需要aop包-->
  <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
  • transaction注解的详细配置
    image

xml的声明式事务

  1. 配置事务管理起
  2. 配置通知
  3. 配置切入点和切面
    image
    事务管理器本质是一个切面类。
 原文:https://www.cnblogs.com/yx-blog/p/15506335.html

相关教程