Spring学习

1、什么是spring框架

spring框架是开发java应用程序的java平台,提供了综合且广泛的基础性支持。其核心是IOC(控制反转)/DI(依赖注入)和aop(面向切面编程),通过IOC容器帮我们管理java bean,通过aop来面向切面编程,使得我们可以在既定的方法上可以灵活的继续织入我们的业务逻辑 而不需要改变之前的业务方法,使用aop来做声明式事务管理、日志、权限控制、异常处理等。

2、为什么要使用spring框架

spring框架本身使用了大量的设计模式,是一个非常优秀且业界使用得较多的一个java基础框架平台,使用他的IoC管理java bean,使用他的aop面向切面编程,spring框架可以很方便的和其他框架集成使用。

3、Spring框架有哪些主要模块?

主要模块有:IOC容器、AOP面向切面编程、数据访问/集成、Web框架(springmvc)、工具、消息、测试模块等。

4、什么是控制反转、是什么依赖注入

控制反转Inversion of Conctrol 简称IOC/依赖注入Dependency Injection 简称DI,
控制反转:对象不再是开发者自己new,而是spring容器(IOC容器)来控制管理java Bean对象。
依赖注入:依赖于容器来创建对象,来为某个对象赋值其依赖的引用。
对象的生命周期由IOC容器管理。底层通过反射实现。

5、解释下Spring框架中的IoC容器

spring的org.springframework.beans包和org.springframework.context包是IOC容器的基础。BeanFactory接口提供了先进的配置机制,使得任何Bean具备配置的可能,ApplicationContext接口对BeanFactory接口进行了扩展,是其子接口,在BeanFactory的基础上增加了很多其他的功能,比如更容易集成Spring的AOP,提供了处理Message Resources的机制(用于国际化)、事件传播、WebApplicationContext等等。

6、BeanFactory和ApplicationContext有什么区别

  • 两个都是IOC容器(定义Bean、对Bean的关联关系的设置、根据请求分发Bean),BeanFactory是老的容器,ApplicationContext是基于BeanFactory更先进的容器。
  • ApplicationContext提供了处理Message Resources的机制(用于国际化)
  • ApplicationContext提供了统一的资源文件读取方式
  • ApplicationContext采取监听器listener的方式注册Bean事件

6、Spring有几种配置方式

将Spring配置到应用开发中有以下三种方式:

  • 基于XML的配置 spring一般推荐使用xml方式
  • 基于注解的配置 springboot推荐使用
  • 基于Java的配置 springboot推荐使用

7、Spring Bean的作用域之间有什么区别

Spring容器中的bean可以分为5个范围:

  • singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
  • prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
  • request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
  • Session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
  • global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中,全局作用域与Servlet中的session作用域效果相同。

8、Spring框架中的单例Beans是线程安全的么

Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Service类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。
最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”
补充:
对于使用过SpringMVC和Struts2的人来说,大家都知道SpringMVC是基于方法的拦截,而Struts2是基于类的拦截。
对于Struts2来说,因为每次处理一个请求,struts就会实例化一个对象;这样就不会有线程安全的问题了。

而Spring的controller默认是Singleton的,这意味着每一个request过来,系统都会用原有的instance去处理,是存在线程安全问题的。
一是我们不用每次创建Controller,二是减少了对象创建和垃圾收集的时间,由于只有一个Controller的instance,当多个线程调用它的时候,它里面的instance变量就不是线程安全的了,会发生窜数据的问题。
当然大多数情况下,我们根本不需要考虑线程安全的问题,比如在controller里注入的dao,service等,除非在bean中声明了实例变量。因此,我们在使用spring mvc 的contrller时,应避免在controller中定义实例变量,否则多线程来调用单例的controller对象时,就可能会对该实例对象造成数据不一致的问题。

解决方法:
1、在Controller中使用ThreadLocal变量,把实例放在ThreadLocal中,每个线程拥有自己独立的实例数据互不影响。
2、在spring配置文件Controller中声明 scope=“prototype”,每次都创建新的controller。

使用spring开发web应用时要注意,默认Controller、Dao、Service都是单例的。

9、如何在Spring中注入一个Java Collection

Spring提供了以下四种集合类的配置元素:

  • < list> : 该标签用来装配可重复的list值或者数组。list放入value
  • < set> : 该标签用来装配没有重复的set值。list放入value
  • < map>: 该标签可用来注入键和值可以为任何类型的键值对 map类型,map里放入entry key value。
  • < props> : 该标签支持注入键和值都是字符串类型的键值对 properties类型,props里放入prop。

10、Spring Bean的自动装配

  • XML配置文件表明了如何根据名称将一个bean设置为自动装配:
    < bean id=“employeeDAO” class=“com.howtodoinjava.EmployeeDAOImpl” autowire=“byName” />
  • 注解方式@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在在Spring配置文件中进行< context:annotation-config />配置才可以使用。
    如果配置了< context:component-scan 自动配置好了< context:annotation-config />
    也可以通过在配置文件中配置AutowiredAnnotationBeanPostProcessor 达到相同的效果。< bean class =“org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor”/>配置好以后也可以使用@Autowired来标注了。

11、Spring 框架中都用到了哪些设计模式

  • 单例模式:在spring配置文件中定义的bean默认为单例模式。
  • 工厂模式:BeanFactory用来创建对象的实例。
  • 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
  • 前端控制器:Srping提供了DispatcherServlet来对请求进行分发。
  • 依赖注入:贯穿于BeanFactory / ApplicationContext接口的核心理念。
  • -代理模式:在AOP和remoting中被用的比较多。

学习实践

注入的表现形式:

  • 在xml里以< bean id="" class="">的形式来实例化一个bean对象,bean里的属性可以通过set方式或构造方法方式来初始化。
    property:
<!-- 演示的依赖注入 	-->
   	<bean id="customerDao" class="com.itheima.day1.demo3.CustomerDaoImpl"/>
   	<bean id="customerService" class="com.itheima.day1.demo3.CustomerServiceImpl">
   		<property name="customerDao" ref="customerDao"/>
   	</bean>
<!-- 注入集合 
		数组、list集合,用<list>
		set用<set>
		map用<map>-->
	<bean id="user" class="com.itheima.day1.demo4.User">
		<property name="arrs">
			<list>
				<value>哈哈</value>
				<value>呵呵</value>
				<value>嘿嘿</value>
			</list>
		</property>
		
		<property name="list">
			<list>
				<value>美美</value>
				<value>小凤</value>
			</list>
		</property>
		<property name="set">
			<set>
				<value>美美</value>
				<value>小凤</value>
			</set>
		</property>
		
		<property name="map">
			<map>
				<entry key="aaa" value="小苍"/>
				<entry key="bbb" value="小泽"/>
				<!-- <entry key-ref="" value-ref=""></entry> -->
			</map>
		</property>
		<!-- Propertis属性文件对象-->
		<property name="pro">
			<props>
				<prop key="username">root</prop>
				<prop key="password">1234</prop>
			</props>
		</property>
	</bean>

构造方法:

	<bean id="person" class="com.itheima.day1.demo4.Person">
   		<constructor-arg name="pname" value="美美"/>
   		<constructor-arg name="car1" ref="car1"/>
   	</bean>
  • 通过注解的方式 常用注解@Autowired、@Resource、@Component、@Service、@Repository、@Controller 来把某个一个类的实例注册到IOC容器中,对象名默认是类名首字母小写。可以通过value来个性化指定。

实例化Bean对象:
1、在spring容器(applicationContext.xml)里通过< bean+java类 成员引用+set方法或构造方法的方式实例化对象。IoC容器初始化完成bean就实例化完成,默认是单例。

2、在spring容器里通过< context:component-scan base-package=“com.itheima”/>上下文以基础包的方式扫描注解组件(@Component、@Service、@Repository、@Controller)的方式实例化对象,不要提供set方法或构造方法。

  • 实例化Bean底层是通过java反射实现的,故任何bean对象,建议都有自己的空构造方法。
  • 如果启动component-scan默认就开启了< context:annotation-config/>
  • spring父容器(IOC容器)一般扫描@Component组件、@Service、@Repository组件
  • spring子容器(springmvc容器)一般扫描@Component、@Controller
	// @Autowired 默认按类型自动装配
	// @Qualifier(value="userDao")	// 如果需要按名称注入联合使用@Qualifier,某个对象和接口有多个实例时需要开发者指定注入哪个实例对象。
	
	// @Resource是Java的注解,Spring框架支持该注解
	// 注解在字段上默认按照字段名寻找(如下使用userDao名字在IOC容器中寻找),注解在get/set方法上,默认按属性名寻找,
	// 如果没有找到,比如UserDAO是:@Repository(value="userDao2"),在IOC容器中Bean的名字为userDao2
	// 按照名字没找到,则自动按照类型来寻找。如果明确指定按照name来寻找,就不会再启动按照类型来寻找匹配了。
	// 如果userDao接口的实现类有多个,则必须使用name指定使用哪个name的Bean对象来注入。
	@Resource
	private UserDao userDao;

	// 明确指定按照名字来寻找bean对象
	@Resource(name="userDaoImpl")
	private UserDao userDaoImpl;

web.xml web容器管理

  • applicationContext.xml spring父容器
  • springmvc.xml spring子容器

在web项目中,我们可以使用web容器来加载spring的父子容器。
在非web项目中,可以使用BeanFactory和ApplicationContext。

Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他们都可以代表Spring容器。

	/**
	 * 老的工厂版本 BeanFactory,已过时
	 * 是一种延迟加载的做法,加载配置文件时并没有实例化对象。
	 */
	@Test
	public void run4(){
		BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
		UserService us = (UserService) factory.getBean("userService");
		us.sayHello();
	}
	/**
	 * 使用的是Spring框架的方式
	 * new ClassPathXmlApplicationContext该对象一旦创建完,xml配置文件里的bean对象就都创建了(包括注解标识的被扫描到容器中的Bean),默认是单例singleton对象。
	 * ApplicationContext是新工厂接口,是对BeanFactory的改造升级。
	 * 服务器启动时,或者new 他的一个实例,就会启动Spring容器,初始化Spring容器时就创建了bean对象。
	 */
	@Test
	public void run2(){
		// 创建工厂,加载核心配置文件
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		// 从工厂中获取到对象,从spring容器中获取bean
		UserServiceImpl usi = (UserServiceImpl) ac.getBean("userService");
		// 调用对象的方法执行
		usi.sayHello();
	}

AOP

Aspect Oriented Programming 面向切面编程。底层通过动态代理来实现。
使用jdk来动态创建代理对象:
可以提供一个工具类,提供一个静态方法,getProxy(dao,切面的逻辑对象)。
通过Proxy类的静态方法newProxyInstance来创建代理对象。

/**
 * 使用JDK的方式生成代理对象
 *
 * 代理对象和实例对象实现了相同的接口,保证了代理对象可以访问和实例的对象一样的方法。
 * @author jobs1127
 *
 */
public class MyProxyUtils {

	public static UserDao getProxy(final UserDao dao) {
		System.out.println("dao.interfaces="+dao.getClass().getInterfaces());
		Class<?>[] interfaces = dao.getClass().getInterfaces();
		for (Class clazz:interfaces) {
			System.out.println(clazz.getName());
		}
		// 使用Proxy类生成代理对象
		UserDao proxy = (UserDao) Proxy.newProxyInstance(
				dao.getClass().getClassLoader(),
				dao.getClass().getInterfaces(),
				// 代理对象调用接口的某个方法就会立即进入到这里
				new InvocationHandler() {
					// 代理对象方法一执行,invoke方法就会执行一次
					// proxy就是代理对象,method就是代理对象正调用的方法,args是方法的参数列表
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						/**
						 * 如果方法名等于save在目标对象调用save()的前后加点代码
						 */
						if ("save".equals(method.getName())) {//代理对象新增的业务
							System.out.println("记录日志 start...");
							// 开启事务
							System.out.println("开启事务");
						}
						
						// 让dao类的save或者update方法正常的执行下去
						Object object = method.invoke(dao, args);//在目标对象上调用方法,执行原本的业务
						
						if ("save".equals(method.getName())) {//代理对象新增的业务
							System.out.println("记录日志 end...");
							// 提交事务
							System.out.println("提交事务");
						}
						return object;
					}
				});
		// 返回代理对象
		return proxy;
	}
}

spring AOP:

  • xml方式:
    1、编写切面类,通过方法描述诸如:前置通知、后置通知、最终通知,环绕通知(ProceedingJoinPoint)等各种通知。
    2、将切面类放入IoC容器中,< aop:config > < aop:aspect ref=“切面类”> < aop:before pointcut="",method=""> aop配置,指定切面,指定通知pointcut拦截到切入点后执行method。
    3、还有种变体:< aop:config > < aop:advisor advice-ref=“advice类” pointcut=“切入点”>,在advice类里讲清楚什么方法执行什么操作。
  • 注解方式:
    1、xml里开启切面相关的注解,< aop:aspectj-autoproxy/>
    2、编写切面类,通过@Aspect标识其为切面,并放入IoC容器中
    3、在切面类里编写各种方法(各种通知)如:@Before(value=“切入点表达式”)
    可以抽取出公共的切入点使用@JoinPoint(value=“切入点表达式”),其他通知使用时通过类名.方法名来引用。
    环绕通知,记住要手动调用切入点的方法,对象:ProceedingJoinPoint

spring是基于容器的管理思想,通过容器来管理Bean对象

数据源

  • spring内置的DataSource:DriverManagerDataSource
  • dbcp数据源:BasicDataSource
  • c3p0数据源:ComboPooledDataSource
  • druid数据源,性能最好,还有性能监控等,常用
  • hakari数据源,性能也很好,在springboot2中默认使用

spring的JdbcTemplate 可以CRUD,需要给其一个DataSource
初始化jdbcTemplate对象后,我们的Dao可以继承JdbcDaoSupport来CRUD
JdbcDaoSupport里有jdbcTemplate属性、还有DataSource属性,初始化DataSource后,也会自动初始化jdbcTemplate

给其一个DataSource,初始化一个平台事务管理器:DataSourceTransactionManager
事务管理模板:TransactionTemplate,通过DataSourceTransactionManager对象初始化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值