Spring的入门学习

什么是Spring

spring是一个轻量级的控制反转(ioc)和面向切面编程(AOP)的容器框架。

  • 轻量:从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的jar文件里发布;并且Spring所需的处理开销也是微不足道的。
  • 非入侵:在应用中,一般不需要引用springjar包里的类
  • 控制反转:Spring的核心机制,通过控制反转技术促进了松耦合。简单来讲,就是把对象创建的权力交给了容器,让容器来管理对象。
  • 面向切面:允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。AOP是基于代理的,通俗一点就是把 核心业务 和 系统服务(日志系统、权限管理等) 分开。

Spring的核心配置和类

  1. applicationContext.xml:核心配置文件。作用:用于配置所有的类,这些类可以称为springbean

  2. BeanFactory:容器的工厂类(接口)。作用:用于创建或获取springbean,即spring管理的对象。

  3. ApplicationContext:应用上下文(接口)他是BeanFactory的子类 作用:用于创建或获取springbean。功能比BeanFactory强大。

    BeanFactoryApplicationContext的区别:

    BeanFactory:懒加载 需要某个对象再去加载

    ApplicationContext:非懒加载 一次性加载所有的对象

Spring IOC

控制反转:把对象的创建、销毁的权利交给容器框架,由容器来管理对象的生命周期。ioc不是新的技术,只是一种思想或理念,实现松耦合。

IOC包括依赖注入(DI,核心) 和 依赖查找。

DI:依赖注入,简单来讲就是在spring实例化对象的时候,由容器来设置这些对象的属性。

spring的注入方式

属性的注入(set方法注入)

前提要有对应的setter方法

以下为spring配置文件代码 java bean忽略。

<bean id="person" class="com.xx.Person">
	<property name = "name" value = "xzy"/>
	<property name = "coll">
		<list>
			<value>list</value>
			<value>list2</value>
		</list>
	</property>
	<property name = "map">
		<map>
			<entry key = "age" value = "21" />
    	</map>
    </property>
    <property name = "arr">
    	<array>
    		<value>java</value>
    		<value>C++</value>
    	</array>
    </property>
</bean>
通过构造器注入

需要构造方法,javaBean代码:

public class User {
    private String name;
    private Integer age;
    public User(String name,Integer age){
        this.name = name;
        this.age = age;
    }
}

配置文件代码:

<!--通过构造器的方式-->
<bean id = "user" class = "cn.pojo.User">
	<constructor-arg value = "Jay" ></constructor-arg>
    <constructor-arg value = "21" />
</bean>
<!--指定下标的方式-->
<bean id="user" class="cn.pojo.User">
		<constructor-arg value="44" index="1"/>
		<constructor-arg value="Jack" index="0"/></bean>
<!--指定在构造中的参数名称-->
<bean id = "user" class = "cn.pojo.User">
	<constructor-arg value="44" name="age" />
    <constructor-arg value="xxx" name = "name" />
</bean>
注入其他类
<!--通过构造器注入-->
<bean id = "user" class = "com.xx.User">
	<constructor-arg value="Jack"></constructor-arg>
	<constructor-arg value="44"></constructor-arg>
    <!--引用的方式 设置引用id-->
    <property name = "car" ref = "car"></property>
</bean>
<bean id = "car" class = "com.xx.Car">
	<property name = "type" value = "BWM"></property>
</bean>

<!--内部声明,用这种方式声明,别的bean不能引用了-->
<property name = "car">
	<bean class = "cn.xx.Car">
    	<property name = "type" value = "红旗"/>
    </bean>
</property>

bean元素中的属性

  • id:Bean的唯一标识符
  • name:通过name对Bean进行管理和配置 name可以多个每个以逗号隔开。
  • class:指定了Bean的具体实现类,必须是完整的类名 实用类的全限定名
  • scope:设定Bean实例的作用域,其属性有singleton(单例)、prototype(原型)、request、session、和global
    Session,默认值为singleton.
  • constructor-arg:的子元素,可以传入构造参数实例化 该元素index属性指定构造参数的序号(从0开始).
  • property:的子元素,通过调用Bean实例中的setter方法完成属性赋值.
  • ref:property、constructor-arg等元素的子元素,该元素中的bean属性用于指定对Bean工厂中某个Bean实例的引用;
  • value:property、constructor-arg等元素的子元素,用来直接指定一个常量值;
  • list:用于封装List或数组类型的依赖注入。
  • set:用于封装Set或数组类型的依赖注入。
  • map:用于封装Map或数组类型的依赖注入。
  • entry:map元素的子元素 用于设定一个键值对。

Bean的实例化

构造器实例化

Spring容器通过Bean对应的默认构造函数来实例化Bean。

静态工厂方式实例化

通过创建静态工厂的方式来创建Bean的实例

public class BeanFactory {
    public static Bean createBean(){
        return new Bean();
    }
}
<!--factory-method-->
<bean id = "bean" class = "com.xx.BeanFactory" factory-method = "createBean"></bean>

实例工厂化方式实例化

不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式.

public class BeanFactory {
    public BeanFactory(){
        System.err.println("BeanFactory 工厂实例化")
    }
    public static Bean createBean(){
        return new Bean();
    }
}

<!--配置工厂-->
<bean id = "beanFactory" class = "com.xx.BeanFactory" /><!--factory-bean 属性指向配置的实例工厂;factory-method属性确定使用工厂的哪个方法-->
<bean id = "bean" factory-bean="beanFactory" factory-method="createBean"/>

Bean的作用域

singleton:单例模式

Spring IOC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象。

配置文件:
<bean id ="dog" class = "com.bean.Dog" scope="singleton"></bean>

java代码:
public void test(){
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    Dog dog1 = (Dog) ctx.getBean("dog");
    System.err.println(dog1); 
    Dog dog2 = (Dog) ctx.getBean("dog");
    System.err.println(dog2);
}

// 输出的结果一致,表明为单例模式。
prototype:原型模式

每次通过Spring容器获取prototype定义的bean时,容器都将创建一个新的Bean实例,每个Bean实例都有自己的属性和状态

request

在一次Http请求中,容器会返回该Bean的同一实例。而对不同的Http请求则会产生新的Bean,而且该bean仅在当前HttpRequest内有效
针对每一次Http请求,Spring容器根据该bean的定义创建一个全新的实例,且该实例仅在当前Http请求内有效,而其它请求无法看到当前请求中状态的变化,当当前Http请求结束,该bean实例也将会被销毁。

session

在一次Http Session中,容器会返回该Bean的同一实例。而对不同的Session请求则会创建新的实例,该bean实例仅在当前Session内有效
同Http请求相同,每一次session请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的session请求内有效,请求结束,则实例将被销毁。

Bean的装配方式

基于XML的装配

两种装配方式:setter注入和构造器注入。

设值注入的两个要求:

  • Bean类必须提供一个默认的午餐构造方法
  • Bean类必须为需要注入的属性提供对应的setter方法

基于注解(annotation)

常见注解:

  • @Component 是所有受Spring管理组件的通用形式。
  • @Repository 持久层使用,dao层使用。
  • @Service 业务类,表示业务层组件,Service层使用。
  • @Controller 控制器,表示web层组件
  • @Autowired 按照类型来装配
  • @Resource 根据名字去装配

自动装配

属性值说明语法
default 默认值由的default-autowire属性值确定default-autowire=“default”
byName根据属性名称自动装配
byType根据属性的数据类型自动装配
constructor根据构造函数参数的数据类型自动装配
no不适用自动装配,必须通过ref元素定义

Spring AOP

实现核心业务系统服务代码之间的分开 通过一种特殊的技术手段来实现核心业务运行时能够实现系统服务的功能;
aop的本质是代理 通过对方法进行拦截、增强来实现的。

AOP的基本概念

采用横向抽取机制,把分散在各个方法中的相同的代码抽取出来,然后再编译器或者是运行时再把这些代码应用到所需要执行的地方。

通知(Advice):aop在切点上执行的增强处理。

通知的类型:

  • 前通知(methodBeforeAdvice):方法执行前做增强
  • 后通知(methodAfterAdvice):方法执行后做增强
  • 环绕通知(MethodInterceptor):方法执行前和后做增强
  • 返回通知(AfterReturningAdvice): 成功返回后 进行增强
  • 异常通知(ThrowsAdvice): 抛出异常后 进行通知

切点(Pointcut):就是带有通知的连接点,就是对哪些类 哪些方法做增强。

切点的类型:

基于正则表达式 JdkRegexpMethodPointcut

基于AspectJ的表达式 AspectJExpressionPointcut

切面(Aspect):通常上就是一个类,里面定义了 通知切点

AOP = 通知 + 切点

AOP案例 java实现

// com.bean.User
public class User {
    public String login(String name){
        return "name is "+ name ;
    }
    public String login(String name , int pwd){
        return "name is "+name+", pwd is "+pwd ;
    }
}

// 新建一个环绕通知
public class MyAroundAdivice implements MethodInterceptor{
    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable{
        System.err.println("方法执行前:"+arg0.getMethod().getName()+","+arg0.getArguments()[0]);
        Object obj = arg0.proceed();
        System.err.println("执行完成后...");
        return obj;
    }
}

// 新建基于AspectJ切点的测试类
@Test
public void test(){
    // 1. 声明通知
    Advice advice = new MyAroundAdivice();
    //2、基于AspectJ声明切点对象
	AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
	//3、设置表达式
	/*返回类型为String类型 com.bean.User的login方法  参数为String类型*/
	//cut.setExpression("execution (String com.bean.User.login(String))");
	//任意放回类型  com包下包括com子包下 任意方法 任意的参数0~n
	cut.setExpression("execution(* com..*(..))");
	//4、切点+通知 =AOP
	Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
	//5、声明代理bean
	ProxyFactory proxy = new ProxyFactory(new User());
	//6、设置aop
	proxy.addAdvisor(advisor);
	//7、从代理中获取代理的对象
	User user = (User) proxy.getProxy();
	user.login("rose");
	System.err.println("---------------");
	user.login("jack",123);
}

AspectJ语法

*这个目录下的,或是类上的所有
任意的多个。0~N个
execution (* com.bean.User.login(String,int))对login方法,必须要接收两个参数,且参数的类型必须是Strig,int的,且返回值无限制。且这个方法必须是User这个类的
Execution (* com..(…))返回所有的类型 所有在com包下的类,不包含子包 类的所有方法 接收任意多个参数 0~N
Execution (* com….(…))在com包下的,包含子包下的所有类的所有方法,所有参数都切入
execution(* com….(String)) || execution(* com….(*,int))|| 逻辑或

AOP案例 基于XML声明式AspectJ

<bean id = "user" class = "com.bean.User">
<!-- 定义一个切面 -->
	<bean id="myBeforeAdvice" class="com.demo03.MyAdvice"></bean>
	<aop:config>
		<!-- 配置切入点 -->
		<!-- 表达式(用来表示方法) -->
		<!-- execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>),返回值,方法名,参数不能少 -->
		<!-- *代表:任意值 方法名:全类名.方法名 参数中的..:任意个数,任意类型 -->
		<aop:pointcut expression="execution(* com..*(..))" id="myPointcut" />
		<!-- 切面配置 -->
		<aop:aspect ref="myBeforeAdvice">
			<!-- 配置前通知 -->
			<aop:before method="doBefore" pointcut-ref="myPointcut" />
			<!-- 配置后通知 -->
			<aop:after method="doAfter" pointcut-ref="myPointcut" />
			<!-- 配置返回通知 -->
			<aop:after-returning method="doReturnAfter"
				pointcut-ref="myPointcut" />
			<!-- 配置环绕通知 -->
			<aop:around method="doAround" pointcut-ref="myPointcut" />
			<!-- 异常通知 -->
			<aop:after-throwing method="doThrowing"
				pointcut-ref="myPointcut" throwing="e" />
		</aop:aspect>
	</aop:config>
</bean>

基于注解的声明式AspectJ (常用)

注解功能
@Aspect注解在类上,声明这是一个切面
@Pointcut注解在方法上声明是一个切点,且要声明aspectj的切点表达式
@Before前通知
@After @AfterRetuning –正确执行成功返回值 @AfterThrow – 错误的执行,抛出异常后通知
@Around环绕通知

第一步:新建注解的切面类

@Aspect
@Component
public class MyAspect {
	//定义一个切入点表达式  使用一个返回值为void,方法体为空的方法来命名切点
	@Pointcut("execution(* com..*(..))")
	public void myPointCut(){}
	
	//前置通知 
	@Before("myPointCut()")
	public void doBefore(JoinPoint joinPoint)
	{
		System.out.println("前置通知,方法开始..."+":目标类是"+joinPoint.getTarget()+",被植入的增强方法是:"+joinPoint.getSignature().getName());
	}
	//后置通知
	@After("myPointCut()")
	public void doAfter()
	{
		System.out.println("后置通知,方法结束...");
	}
	//返回通知
	@AfterReturning("myPointCut()")
	public void doReturnAfter()
	{
		System.out.println("返回通知,方法最终结束...");
	}
	/**
	 * 环绕通知  ProceedingJoinPoint用来调用被增强的方法 
	 * 必须是Object的返回类型
	 * 必须接受一个参数,类型为ProceedingJoinPoint
	 * 必须throws Throwable
	 */
	@Around("myPointCut()")
	public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
	{
		System.out.println("环绕通知:begin...");
		 //执行被增强的方法
		Object obj = joinPoint.proceed();
		System.out.println("环绕通知:end.....");
		return obj;
	}
	@AfterThrowing(value="myPointCut()",throwing="e")
	public void doThrowing(Throwable e){
		System.out.println("异常通知......."+e.getMessage());
	}
}

第二步:xml配置文件

<bean id="user" class="com.bean.User"></bean>
<context:component-scan base-package="com.xxx"></context:component-scan>
	<!-- 启动基于注解的声明式AspectJ支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

第三步:测试方法

@Test
public void test1() {
	ApplicationContext ctx = new ClassPathXmlApplicationContext("com/xxx/applicationContext.xml");
	User user = ctx.getBean("user",User.class);
	user.say();
	//user.run();
}

如有问题 请指出

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值