spring01

spring

  • ssh: struts2+hibernate+spring
  • ssm: spring+springmvc+mybatis

问题:什么是spring?

答:spring是一个轻量级的J2EE框架,它可以让java的企业级开发变的非常简单

问题:spring有哪些版本?

答:1.2,2.0,2.5,3.0,3.1,4.0,4.2,5.x

我们学习阶段:刚开始使用:4.2,使用maven以后版本改为4.37,在微服务架构中我们使用 5.1.6

spring提倡的一个思想,组件与组件之间尽量做到低耦合


准备工作:

在idea中,配置applicationContext.xml文件的模板

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
		http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-4.2.xsd 
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">


</beans>    

spring的核心概念:

IOC : 控制反转

它是一种思想,它是指创建对象、管理对象的方式,以前是自己创建自己管理,现在变成由spring容器创建,容器进行管理

DI  : 依赖注入

它是IOC的具体实现,有了spring以后,类中属性的初始化,依赖于spring容器注入的参数,才可以完成初始化,这一个
过程就称为:“依赖注入”

AOP :面向切向编程

在没有spring之间,假设:一个层次要调用另一个层次的方法(例如:Service调用Dao层的方法),可以在一个类,
自己创建另一个类的实例  

public class InfService {

	private InfDao dao;

	public void setDao(InfDao dao) {
	    this.dao = dao;
	}

	public void addData(){
	    dao.add();
	}
}

@@@@@@@:这种new对象的写法,属于一种高耦合的写法

在spring应用中,不提倡,调用者自己创建被调用者的实例,在spring的应用中,所有对象都是由spring容器来创建,
哪里,需要这个对象, spring容器就会把对象传递到哪里

-----------------------------------------------------------------------
任务1:通过spring实现hello world

步骤:

1、创建一个java工程 

2、导入jar    
spring 4.2.4
core,beans,context,expression

sprign 3.0.2
logging  (spring-framework-3.0.2.RELEASE-dependencies\org.apache.commons\com.springsource.org.apache.commons.logging\1.1.1)

3、生成spring的主配置文件  applicationContext.xml

4、创建InfDao

package org.java.dao;

public class InfDao {

    public void add(){
        System.out.println("向mysql添加了数据");
    }
}

5、在applicationContext.xml文件中,注册InfDao

<bean id="infDao" class="org.java.dao.InfDao"/>

6、创建InfService

public class InfService {

	private InfDao dao;

	public void setDao(InfDao dao) {
	    this.dao = dao;
	}

	public void addData(){
	    dao.add();
	}
}

7、在applicationContext.xml文件中,配置InfService,并且配置要给当前对象注入的对象

<bean id="infService" class="org.java.service.InfService">
    <property name="dao" ref="infDao"/>
</bean>

8、编写测试类进行测试

package org.java.demo;

import org.java.service.InfService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo {

    public static void main(String[] args) {

        ApplicationContext cxf = new ClassPathXmlApplicationContext("applicationContext.xml");

        InfService service = (InfService) cxf.getBean("infService");

        service.addData();
    }
}

ApplicationContext 与  applicationContext.xml是什么关系 ?

ApplicationContext:它是spring容器,它负责产生对象,只有在applicationContext.xml文件中,注册过的对象,它才能产生

问题:为什么要使用依赖注入DI

答:目的,将组件的创建与使用强制分开,降低组件与组件之间的耦合度

我们要使用某一个组件,只需要声明即可,不需要关心该组件如合创建,在运行时,spring容器会把创建好的组件
自动注入到需要它的地方

DI:它是一种组件化的思想,它可以将一个复杂应用,按功能拆分

spring应用,提倡面向接口编程:

  1. 可以隐藏实现细节
  2. 使用接口可以方便更换程序中的业务逻辑

注意:一般Service,Dao这两层都要写接口

接口的写法:

书上的规范: 

  • 接口名: org.java.dao.IDao
  • 实现类: org.java.dao.Dao

开发中的写法:

  • 接口名:  org.java.dao.InfDao
  • 实现类: org.java.dao.impl.InfDaoImpl


spring:按注入的数据类型,可以分为:

1、对象的注入  ref

            <bean id="infService" class="org.java.service.impl.InfServiceImpl">
                <property name="dao" ref="infDao"/>
            </bean>

2、值的注入  value

            <bean id="infService" class="org.java.service.impl.InfServiceImpl">
                <property name="dao" ref="infDao"/>
                <property name="msg" value="hello world"/>
                <property name="score" value="100"/>
            </bean>

3、集合的注入 (List,Map,Set)        

@@@list:
            <bean id="infService" class="org.java.service.impl.InfServiceImpl">
            <property name="list">
                <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
                </list>
            </property>
            </bean>

        @@@@Set

            <bean id="infService" class="org.java.service.impl.InfServiceImpl">
            <property name="arr">
                <set>
                <value>111</value>
                <value>222</value>
                <value>333</value>
                </set>
            </property>
            </bean>


        @@@@ Map

            <bean id="infService" class="org.java.service.impl.InfServiceImpl">
            <property name="map">
                <props>
                <prop key="msg1">one.....</prop>
                <prop key="msg2">two.....</prop>
                </props>
            </property>
            </bean>
  • 值:value
  • 对象: ref
  • 集合:list,set,props

spring按注入的数据的方式,又可分为:

1、依赖注入

        @@@:这种方式是通过属性的set方法进行注入

           private InfDao dao;

            public void setDao(InfDao dao) {
            this.dao = dao;
            }

2、构造注入

        @@@:这种方式是通过构造方法进行注入
        需要在类中,编写一个带参数的构造方法

          public InfService(InfDao dao,String msg, int score) {
            this.dao = dao;
            }

            <bean id="infService" class="org.java.service.InfService">
            <constructor-arg index="0" ref="infDao"/>
            <constructor-arg index="1" value="hello"/>
            <constructor-arg index="2" value="100"/>
            </bean>

3、自动绑定注入

@@@@@:这种方式注入,属性依然要生成set方法

<bean id="infService" class="org.java.service.InfService" autowire="byType"/>

这种写法,需要什么类型,自己在spring容器去找对应的类型,然后自己注入

  • utowire="byType"-------这种方式是按类型,自动注入,要求:匹配类型只有一个才可以使用
<bean id="infService" class="org.java.service.InfService" autowire="byName"/>
  • 这种方式按名称匹配,自动注入,允许有多个匹配类型

在spring应用中,为了简化开发过程,简化xml文件中的代码,spring提供了一种注解的方式

Annotations: 注释,注解

使用spring注解(spring注释)的好处:
    

  1. 代码会更为简单
  2. 简化了xml文件的代码,很多配置无需在xml文件中执行
    对象不需要在xml文件中注册
    对象之间的依赖关系,也不需要在xml文件中进行配置
  3. 对象的注入,不需要写set方法

采用spring注解的方式配置程序:

步骤:

    1、创建java工程 

    2、导入jar
        spring 4.2  
            core,beans,context,expression

            增加: aop,aspects
        
        spring 3.0.2
            logging

            增加:  aop联盟    aopalliance,
                3.0.2.RELEASE-dependencies\org.aopalliance\com.springsource.org.aopalliance\1.0.0

                   织入的包 weaver
                   3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE

    3、编写applicationContext.xml

    4、在xml文件启用spring注解   
        <context:annotation-config/>

spring中的注解符号:

  • <context:annotation-config/>  该符号启用spring注解
  • <context:component-scan base-package="org.java"/>  组件扫描,扫描org.java包下面所有加了注解标识的类

由于<context:component-scan base-package="org.java"/>它是继承于<context:annotation-config/>

所以,只要采用组件扫描,<context:annotation-config/>这个标记,可以省略不写

  • 数据访问层(Dao)------------------   @Repsoitory
  • 业务逻辑层 (service)---------------  @Service
  • 控制层  (Web) ------------------  @Controller
  • 早期的用于标识组件的注解符号是: @Compoment (它可以表示:数据访问层,业务逻辑层,控制层)
@Autowired
private InfDao dao;-----------此代码,表示,按类型自动注入byType
@@@@@@@@如果采用这一种方式,注入,要求,匹配的类型,只能有一个

@Autowired
@Qualifier("dao2")
private InfDao dao; ----------此代码,表示,按照名称,自动注入 byName

在使用spring注解时,如果没有给当前组件指定别名,系统将生成,默认的别名:

生成的原则如下: 类名,首字母小写

注意:一旦给组件指定别名,默认别名就失效

AOP:面向切面编程(面向方面编程)

问题1:什么是aop?

答:AOP称为“面向切面编程”,也称为“面向方面编程”

问题2:aop要解决什么样的问题?

答:aop用于集中处理程序中涉及到公共性的一些辅助功能,让程序员集中精力编写业务逻辑

问题3:aop通过什么方式来实现?

答:aop主要是通过一种“通知”的形式来实现。
通知的类型有四种:它们会在不同的时候,自动的调用

  • 前置通知、后置通知、异常通知、环绕通知

问题4:通知应该如何配置?

答:要配置通知,我们要通过一种"代理模式"的方式进行配置

问题5:为什么要用代理模式?(proxy)

答:通过代理模式,可以在实现原有功能的基础上,附加额外功能

  •  核心任务:将程序中涉及到的公共问题,集中进行处理

将程序中,涉及到的一些辅助性的一些公共问题,集中编写成一个公共的模块,通过一定的配置,当需要这些功能的时候,
系统将会自动进行调用


AOP实现方式,主要是通过一种"通知"的方式进行实现 (Advice)

spring的AOP提供了四种类型的通知,分别在不同的时候自动运行:

  1. 前置通知--------------------配置在前置通知的代码,将会在业务逻辑执行之前自动调用 
  2. 后置通知--------------------配置在后置通知的代码,将会在业务逻辑执行之后自动调用 
  3. 异常通知--------------------配置在异常通知的代码,将会在业务逻辑执行过程中,产生异常时执行
  4. 环绕通知--------------------它把整个业务逻辑执行过程包含在通知中

@@@@@@@@@如何配置通知
    在AOP中,配置通知,需要使用一种设计模式:代理模式(proxy)

代理模式:通过代理可以在实现原有功能的基础上,附加额外功能

@@@@@@@:使用代理模式的目的,要在原有功能的基础上,附加额外功能

示例:

applicationContext.xml

    <!--业务类-->
    <bean id="buyBookService" class="org.java.service.impl.BuyBookServiceImpl"/>

    <!--声明通知 -->
    <bean id="before" class="org.java.advice.BeforeAdvice"/>
    <bean id="after" class="org.java.advice.AfterAdvice"/>

    <!--配置代理类-->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--指定访问哪一个接口时,进行代理-->
        <property name="proxyInterfaces">
            <value>org.java.service.BuyBookService</value>
        </property>
        <!--指定,要附加的功能-->
        <property name="interceptorNames">
            <list>
                <value>before</value>
                <value>after</value>
            </list>
        </property>
        <!--指定,功能附加完成以后,要调用的业务类-->
        <property name="target" ref="buyBookService"/>
    </bean>

AfterAdvice.java

package org.java.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("");
        System.out.println("------后置通知----------");
    }
}

BeforeAdvice.java

package org.java.advice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 前置通知,此处的代码,在执行业务逻辑代码之前,自动运行
 */
public class BeforeAdvice implements MethodBeforeAdvice {

    /**
     *
     * @param method:即将执行的是类中的哪一个业务方法
     * @param objects:调用方法时,要传递给方法的参数有哪些
     * @param o:即将要执行的是哪一个业务逻辑类
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("-------进入前置通知类-------------");
        System.out.println("即将访问的业务类是:"+o.getClass());
        System.out.println("即将访问的业务方法是:"+method.getName());
        System.out.println("即将传递给方法的参数有:"+ Arrays.toString(objects));
        System.out.println("-------离开前置通知类-------------");

        System.out.println("");
    }
}

BuyBookServiceImpl.java

package org.java.service.impl;

import org.java.service.BuyBookService;

/**
 * 业务实现类
 */
public class BuyBookServiceImpl implements BuyBookService {

    @Override
    public String buyBook(String who, String bookName) {
        System.out.println("-----------------进入业务逻辑类------------------");

        System.out.println("正在进行业务逻辑处理");

        System.out.println("-----------------离开业务逻辑类------------------");
        return who+",购买了书:"+bookName;
    }
}

测试

package org.java.demo;

import org.java.service.BuyBookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo {

    public static void main(String[] args) {

        ApplicationContext cxf = new ClassPathXmlApplicationContext("applicationContext.xml");

        BuyBookService service = (BuyBookService) cxf.getBean("proxy");

        service.buyBook("张三","环球地理");
    }
}

前置通知:它会在业务逻辑类调用之前先执行,前置通知需要实现接口: MethodBeforeAdvice

后置通知:它会在业务逻辑类调用之后执行,后置通知,需要实现接口:AfterReturningAdvice

异常通知:它会在业务逻辑类执行过程中,如果产生了异常才会被调用,异常通知,需要实现接口ThrowsAdvice

异常通知中,没有强制实现的方法,但我们写的方法要满足以下规则:

    public void afterThrowing(Method method,Object[] args,Object target,Exception ex ){
    
    }

环绕通知:它把整个业务逻辑类的执行包含在通知里面,环绕通知,一般实现:MethodInterceptor


目前,这一种代理方式存在的问题:

  • 问题1: 针对于每一个业务类配置一个代理,如果有1000个业务类,就需要配置1000个代理类
  • 问题2: 这种方式配置代理,每一次都需要访问代理类的名称,才能有附加功能

解决方案1:

采用自动代理

优点:

  1. 代理由系统根据访问的业务的名称,自动创建代理
  2. 直接访问业务类的名称即可,不用访问代理类的名称
        <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!--要附加的功能-->
        <property name="interceptorNames">
            <list>
            <value>before</value>
            <value>after</value>
            <value>mythrows</value>
            </list>
        </property>
        <!--配置对哪些业务类进行代理-->
        <property name="beanNames">
            <list>
            <value>buyBookService</value>
            <value>service2</value>
            <value>service3</value>
            </list>
        </property>
        </bean>

解决方案2:

声明式代理

优点:

  1. 对某一个包路径下面的所有业务类,形成代理(无需修改代码)
  2. 直接访问业务类的名称即可,不用访问代理类的名称
  3. 采用声明式代理,通知写在一个类中即可,这个类,无需实现任何接口 
    <!--AOP配置-->
        <aop:config>
        <!--配置切入点(触发代理的条件)-->
        <aop:pointcut id="beanNames" expression="execution(* org.java.service.*.*(..))"/>
        <!--配置切面-->
        <aop:aspect ref="advice">
            <!--<aop:before method="before" pointcut-ref="beanNames"/>-->
            <!--<aop:after-returning method="after" returning="returnValue" pointcut-ref="beanNames"/>-->
            <!--<aop:after-throwing method="mythrowing" pointcut-ref="beanNames" throwing="ex"/>-->
            <aop:around method="round" pointcut-ref="beanNames"/>
        </aop:aspect>
        </aop:config>

解决方案3:采用注解的方式配置


        <!--组件扫描-->
        <context:component-scan base-package="org.java"/>

        <!--指定,采用注解的方式,配置通知-->
        <aop:aspectj-autoproxy/>

        @Before(value = "execution(* org.java.service.*.*(..))")
        public void before(JoinPoint jp){
            System.out.println("------------------------进入前置通知");
        }


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值