学习笔记 --Spring 框架

本贴最后更新于 2605 天前,其中的信息可能已经事过景迁

//概述

轻量级,一站式,开发框架

IoC,Inversion of Control,控制反转

DI,Dependency Injection,依赖注入

AOP,Aspect-Oriented Programming,面向切面编程:业务逻辑与非业务逻辑分离,如日志、安全...

IoC 容器:

对象创建、装配

对象生命周期管理

上下文环境

//IoC 容器

IoC = ApplicationContext (org.springframework.context, spring-context)

初始化

ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");

ApplicationContext context = new FileSystemXmlApplicationContext("/home/user/conf/application-context.xml");

或在 web.xml 中

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:application-context.xml</param-value>
</context-param>

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Bean 定义

<?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"
	xmlns:p="http://www.springframework.org/schema/p"
	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">

	<bean id="screwDriver" class="com.netease.course.ScrewDriver"></bean>

</beans>

Bean 使用

//初始化容器

ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");

//获取对象

ScrewDriver screwDriver = context.getBean("screwDriver", ScrewDriver.class);

//使用对象

screwDriver.use();

Bean 作用域

singleton,单例

<bean id="screwDriver" class="com.netease.course.ScrewDriver" scope="singleton"></bean>

默认为单例
prototype,每次引用创建一个实例

<bean id="screwDriver" class="com.netease.course.ScrewDriver" scope="prototype"></bean>

request scope, requestBean
session scope, sessionBean
application scope, appBean
global scope

Bean 生命周期回调

创建,申请资源
可以通过实现接口

public interface InitializingBean {
	void afterPropertiesSet() throws Exception;
}

或者直接在 application-context.xml 中配置

<bean id="screwDriver" class="com.netease.course.ScrewDriver" init-method="init"></bean>

对应代码

public class ScrewDriver {
   public void init() {
		System.out.println("Init screwDriver");
   }
}

销毁
可以通过实现接口

public interface DisposableBean {
	void destroy() throws Exception;
}

或者直接在 application-context.xml 中配置

<bean id="screwDriver" class="com.netease.course.ScrewDriver" destroy-method="cleanup"></bean>

对应代码

public class ScrewDriver {
   public void cleanup() {
		System.out.println("Cleanup screwDriver");
   }
}

关闭 Bean

((ConfigurableApplicationContext) context).close();

依赖注入

构造函数,强依赖

Setter 函数,可选依赖

配置 bean 的类的构造函数的参数

 <bean id="straightHeader" class="com.netease.course.StraightHeader">
	  <constructor-arg value="red"></constructor-arg>
	  <constructor-arg value="15"></constructor-arg>
  </bean>

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg index="0" value="red"></constructor-arg>
    <constructor-arg index="1" value="15"></constructor-arg>
</bean>

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg type="java.lang.String" value="red"></constructor-arg>
    <constructor-arg type="int" value="15"></constructor-arg>
</bean>

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg name="color" value="red"></constructor-arg>
    <constructor-arg name="size" value="15"></constructor-arg>
</bean>

需要传递集合类型的构造函数参数(如 map)时

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg>
        <map>
            <entry key="color" value="red"></entry>
            <entry key="size" value="15"></entry>
        </map>
    </constructor-arg>
    <constructor-arg name="size" value="15"></constructor-arg>
</bean>

传入 list 时

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg>
        <list>
            <value>red</value>
            <value>15</value>
        </list>
    </constructor-arg>
    <constructor-arg name="size" value="15"></constructor-arg>
</bean>

传入 Properties 时

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg>
        <props>
            <prop key="color">red</prop>
            <prop key="size">15</prop>
        </props>
    </constructor-arg>
    <constructor-arg name="size" value="15"></constructor-arg>
</bean>

从外部倒入配置时

  <bean id="straightHeader" class="com.netease.course.StraightHeader">
	  <constructor-arg name="color" value="${color}"></constructor-arg>
	  <constructor-arg name="size" value="${size}"></constructor-arg>
  </bean>
  <bean id="headerProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location" value="classpath:header.properties" />
</bean>

在一个 bean 中添加所依赖的 bean

<bean id="screwDriver" class="com.netease.course.ScrewDriver">
    <constructor-arg>
        <ref bean="straightHeader" />
    </constructor-arg>
</bean>

通过 Setter 方法注入依赖

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <property name="color" value="${color}"></property>
    <property name="size" value="${size}"></property>
</bean>

自动装配

constructor 是按 byType 方式注入

   <bean id="screwDriver" class="com.netease.course.ScrewDriver" autowire="constructor">
	</bean>

   <bean id="screwDriver" class="com.netease.course.ScrewDriver" autowire="byName">
	</bean>

 <bean id="screwDriver" class="com.netease.course.ScrewDriver" autowire="byType">
  </bean>

Annotation

@Component:定义 Bean,或 @Component("name")

@Value:properties 注入

@Autowired & @Resource:自动装配依赖

@PostConstruct & @PreDestroy:生命周期回调

在 xml 中加入

 <context:component-scan base-package="com.netease.course" />

//AOP 技术

AOP 术语

Aspect:日志、安全等功能

Join point:函数执行或者属性访问

Advice:在某个函数执行点上要执行的切面功能

Pointcut:匹配横切目标函数的表达式

Advice 类型

Before:函数执行之前

After returning:函数正常返回之后

After throwing:函数抛出异常之后

After finally:函数返回之后

Around:函数执行前后

Spring AOP

非完整 AOP 实现

整合 AOP 和与 IoC

XML schema-based AOP

@AspectJ annotation-based AOP

@AspecsJ annotation-based AOP

aspectjweaver.jar

<?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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<aop:aspectj-autoproxy />

</beans>

定义 Aspect

   <bean id="loggingAspect" class="com.netease.course.LoggingAspect">
		   <!-- configure properties of aspect here as normal -->
   </bean>

相应代码中类名前加入 @Aspect

定义 Pointcut

@Pointcut("execution(* com.netease.course.Calculator.*(..))")

private void arithmetic() {}

Pointcut 表达式

designator(modifiers? return-type declaring-type? name(param) throws?)

designator: execution, within

modifiers: public, private

return-type: 返回类型,*

declaring-type: 包名,类名

name: 函数名,*

param: 参数列表,()无参,(..)任意参数

throws: 异常类型

可以组合

定义 Advice

@Before("com.netease.course.LoggingAspect.arithmetic()")

public void doLog() {

  //

}

@Before("execution(* com.netease.course.Calculator.*(..))")

public void doLog() {

    //

}

@AfterReturning("com.netease.course.LoggingAspect.arithmetic()")

public void doLog() {

    //

}

@AfterThrowing("com.netease.course.LoggingAspect.arithmetic()")

public void doLog() {

    //

}

@After("com.netease.course.LoggingAspect.arithmetic()")

public void doLog() {

    //

}

Advice 参数

函数上下文信息

@Before("com.netease.course.LoggingAspect.arithmetic()")

public void doLog(JoinPoint jp) {

    System.out.println(jp.getSignature() + ", " + jp.getArgs());

}

@Around("com.netease.course.LoggingAspect.arithmetic()")

public void doLog(ProceedingJoinPoint pjp) {

    System.out.println("start method: " + pjp.toString());

    Object retVal = pjp.proceed();

    System.out.println("stop method: " + pjp.toString());

}

返回值

@AfterReturning(pointcut="com.netease.course.LoggingAspect.arithmetic()"),

                returning="retVal")

public void doLog(Object retVal) {

    //

}

异常

@AfterThrowing(pointcut="com.netease.course.LoggingAspect.arithmetic()"),

               throwing="ex")

public void doLog(IllegalArgumentException ex) {

    //

}

目标函数参数

@Before("com.netease.course.LoggingAspect.arithmetic() && args(a, ..)")

public void doLog(JoinPoint jp, int a) {

    //

}

XML schema-based AOP

定义 Aspect 和 PointCut

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:pointcut id="arithmetic" expression="execution(* com.netease.course.Calculator.*(..))" />
	 </aop:aspect>
 </aop:config>

定义 Advice

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:before pointcut-ref="arithmetic" method="doLog" />
	 </aop:aspect>
 </aop:config>

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:before pointcut="execution(* com.netease.course.Calculator.*(..))" method="doLog" />
	 </aop:aspect>
 </aop:config>

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:after-returning pointcut-ref="arithmetic" returning="retVal" method="doLog" />
	 </aop:aspect>
 </aop:config>

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:after-throwing pointcut-ref="arithmetic" throwing="ex" method="doLog" />
	 </aop:aspect>
 </aop:config>

   <aop:aspect id="loggingAspect" ref="loggingBean">
       <aop:around pointcut-ref="arithmetic" method="doLog" />
   </aop:aspect>

//数据访问

DAO,Data Access Object

数据访问相关接口

ORM,Object Relation Mapping

对象关系映射

DataSource (javax.sql)

DriverManagerDataSource (org.springframework.jdbc.datasource)

BasicDataSource (org.apache.commons.dbcp)

   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	   <property name="driverClassName" value="${jdbc.driverClassName}" />
	   <property name="url" value="${jdbc.url}" />
	   <property name="username" value="${jdbc.username}" />
	   <property name="password" value="${jdbc.password}" />
   </bean>

   <context:property-placeholder location="db.properties" />

JdbcTemplate (org.springframework.jdbc.core)
设置 JdbcTemplate 的数据库配置信息

private JdbcTemplate jdbcTemplate;

@Autowired
public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
}

查询操作

    int rowCount = this.jdbcTemplate.queryForObject("select count(*) from user", Integer.class);
    int countOfNamedJoe = this.jdbcTemplate.queryForObject("select count(*) from user where first_name = ?", Integer.class, "Joe");
    String lastName = this.jdbcTemplate.queryForObject("select last_name from user where id = ?", new Object[]{1212L}, String.class);

更改操作

this.jdbcTemplate.update("insert into user (first_name, last_name) values (?, ?)", "Meimei", "Han");
this.jdbcTemplate.execute("create table user (id integer, first_name varchar(100), last_name varchar(100))");

对象匹配

    User user = this.jdbcTemplate.queryForObject("select last_name from user where id = ?",
            new Object[]{1212L},
            new RowMapper<User>() {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setFirstName(rs.getString("first_name"));
            user.setLastName(rs.getString("last_name"));
            return user;
        }
    });

    List<User> users = this.jdbcTemplate.query("select last_name from user where id = ?",
            new Object[]{1212L},
            new RowMapper<User>() {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setFirstName(rs.getString("first_name"));
            user.setLastName(rs.getString("last_name"));
            return user;
        }
    });

定义 JdbcTemplate

public class JdbcExampleDao implements ExampleDao {
	private JdbcTemplate jdbcTemplate;
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}
	//...DAO接口实现
}

可以在 xml 中配置相应的 bean
也可以用 annotation 的方法,如下:

@Repository
public class JdbcExampleDao implements ExampleDao {
	private JdbcTemplate jdbcTemplate;
	@Autowired
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}
	//...DAO接口实现
}

NamedParameterJdbcTemplate (org.springframework.jdbc.core)

private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

@Autowired
public void setDataSource(DataSource dataSource) {
    this.namedParameterJdbcTemplate= new NamedParameterJdbcTemplate (dataSource);
}

public int countOfUserByFirstName(String firstName) {
	String sql = "select count(*) from usertest where first_name = :first_name";
	Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
	return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}

其他 api 接口

queryForObject(String sql, Map<String, ?> paramMap, RowMapper<T> rowMapper)
queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType)

SqlParameterSource: MapSqlParameterSource, BeanPropertySqlParameterSource (org.springframework.jdbc.core.namedparam)
如:
public int countOfUserByFirstName(User user) {
	String sql = "select count(*) from usertest where first_name = :first_name";
	SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(user);
	return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}

异常处理

DataAccessException, "unchecked", 是一个基类 (org.springframework.dao)

//事务管理

spring 事务管理

统一的事务编程模型,编程式事务及声明式事务(AOP)

public interface PlatformTransactionManager {

	TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;

}

事务管理器

PlatformTransactionManager (org.springframework.transaction):

DataSourceTransactionManager (org.springframework.jdbc.datasource), JDBC

HibernateTransactionManager (org.springframework.orm.hibernate), Hibernate

TransactionDefinition

getName:事务名称

getIsolationLevel:隔离级别

getPropagationBehavior:传播行为

getTimeout:超时时间

isReadOnly:是否只读

TransactionStatus

isNewTransaction:是否是新事务

hasSavePoint:是否有 savepoint(诊断,NESTED)

isCompleted:是否完成

isRollbackOnly:事务结果是否是 rollback-only
setRollbackOnly:设置事务为 rollback-only

隔离级别

ISOLATION_READ_UNCOMMITTED:读未提交

ISOLATION_READ_COMMITTED:读提交

ISOLATION_REPEATABLE_READ:重复读

ISOLATION_SERIALIZABLE:串行化

ISOLATION_DEFAULT:默认

传播行为

PROPAGATION_MANDATORY:必须在一个事务中运行,不存在则抛异常

PROPAGATION_NEVER:不应该在事务中运行,存在则抛异常

PROPAGATION_NOT_SUPPORTED:不应该在事务中运行,存在则挂起

PROPAGATION_SUPPORTS:不需要事务,有则在事务中执行

PROPAGATION_REQUIRED:必须在事务中执行,如果不存在,则启动新事务(内部事务会影响外部事务)

PROPAGATION_NESTED:必须在事务中执行,如果不存在,则启动新事务(事务之间互不影响)

PROPAGATION_REQUIRES_NEW:必须在新事务中执行,挂起当前事务(独立 physical 事务)

声明式事务

添加 schema

<?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: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-2.0.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

定义事务管理器

<bean id="txManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>
<context:property-placeholder location="db.properties" />

定义事务 Advice

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true" />
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>

定义 Pointcut

<aop:config>
    <aop:pointcut id="daoOperation"
        expression="execution(* com.netease.course.AccountDao.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="daoOperation" />
</aop:config>

配置 <tx:method />

name: 匹配的函数名称,支持*匹配
propagation:事务传播行为
isolation:事务隔离级别
timeout:超时
read-only:是否只读事务
rollback-for:触发回滚的异常,用逗号分隔
no-rollback-for:不触发回滚的异常

采用 annotation 方法
@Transactional
xml 中添加

<tx:annotation-driven transaction-manager="txManager" />

相应代码

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public boolean deleteClusterByClusterId(Sring clusterId) {
	// do work
}

@transactional
value:使用的 TransactionManager
propagation:事务传播行为
isolation:事务隔离级别
timeout:超时
readOnly:是否只读
rollbackFor:触发回滚的异常类对象数组
rollbackForClassName:触发回滚的异常类名称数组
noRollbackFor:不触发回滚的异常类对象数组
noRollbackForClassName:不触发回滚的异常类名称数组

编程式事务

定义 TransactionTemplate

public class SimpleService implements Service {
	private final TransactionTemplate transactionTemplate;
	public SimpleService(PlatformTransactionManager transactionManager) {
		this.transactionTemplate = new TransactionTemplate(transactionManager);
		this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
		this.transactionTemplate.setTimeout(30);
	}
}

使用 TransactionTemplate

public Object someMethod() {
    return transactionTemplate.execute(new TransactionCallback() {
        public Object doInTransaction(TransactionStatus status) {
            updateOperation1();
            return resultOfUpdateOperation2();
        }
    });
}

或(不返回结果)

public Object someMethodWithoutResult() {
    return transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            updateOperation1();
            updateOperation2();
        }
    });
}

或(设置为遇到异常时只能回滚)

public Object someMethodWithoutResult() {
    return transactionTemplate
            .execute(new TransactionCallbackWithoutResult() {
                protected void doInTransactionWithoutResult(
                        TransactionStatus status) {
                    try {
                        updateOperation1();
                        updateOperation2();
                    } catch (SomeBusinessException e) {
                        status.setRollbackOnly();
                    }
                }
            });
}

PlatformTransactionManager 的实现

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("TxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = txManager.getTransaction(def);
try {
    //do something
} catch (MyException ex) {
    txManager.rollback(status);
    throw ex;
}
txManager.commit(status);

整合 MyBatis

SqlSessionFactory
添加 mybatis-spring 依赖

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.3.0</version>
    </dependency>

定义 SqlSessionFactoryBean

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:sqlMapConfig.xml" />
    <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" />
</bean>

定义 Mapper

public interface UserMapper {

	@Select("SELECT * FROM users WHERE id = #{userId}")
	User getUser(@Param("userId") String userId);
}

配置结果映射

@Results({
    @Result(property="id", column="id"),
    @Result(property="firstName", column="first_name"),
    @Result(property="lastName", column="last_name")
})

或者采用 xml 的方法,见 Mybatis 部分

定义 Mapper Bean

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.netease.course.UserMapper" />
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

或采用自动发现的机制

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://mybatis.org/schema/mybatis-spring
	http://mybatis.org/schema/mybatis-spring.xsd">

	<mybatis:scan base-package="com.netease.course" />
</beans>

当需要指定 SqlSessionFactory 时

<mybatis:scan base-package="com.netease.course" factory-ref="sqlSessionFactory" />

或者

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.netease.course" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>

使用 Mapper

public class SomeService {
	@Autowired
	private UserMapper userMapper;
	public User getUser(String userId) {
		return userMapper.getUser(userId);
	}
}

定义 SqlSessionTemplate 使用

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

SqlSessionTemplate 使用

public class UserDao {
	@Autowired
	private SqlSession sqlSession;
	public User getUser(String userId) {
		return (User) sqlSession.selectOne("com.netease.course.UserMapper.getUser", userId);
	}
}

//Web 框架

DispatcherServlet

[servlet-name]-servlet.xml
HandlerMapping
Controllers
View 解析相关

WebApplicationContext

ContextLoaderListener

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>

Servlet WebApplicationContext (containing controllers, view resolvers, and other web-related beans)
Root WebAppliacationContext (containing middler-tier services, datasources, etc.)

实现 Controller

  @Controller
  @RequestMapping(value = "/hello")
  public class HelloController {
	  @RequestMapping(value = "/spring")
	  public void spring(HttpServletResponse response) throws IOException {
		  response.getWriter().write("Hello, Spring Web!!");
	  }
  }

定义 Controller

自动发现

 <context:component-scan base-package="com.netease.course" />

@RequestMapping
name: 名称
value & path: 路径,如"/hello"
method: 请求方法,如"GET"
params: 请求参数
headers: 请求头
consumes: 请求的媒体类型,"Content-Type"
produces: 响应的媒体类型,"ACCEPT"

注入路径中的变量

@RequestMapping(value="/users/{userId}")
public String webMethod(@PathVariable String userId) {
    //do work
}

@RequestMapping(value="/users/{userId:[a-z]+}")
public String webMethod(@PathVariable String userId) {
    //do work
}

函数参数
HttpServletRequest / HttpServletResponse, HttpSession (Servlet API)
Reader / Writer
@PathVariable
@RequestParam
@RequestHeader
HttpEntity
@RequestBody
Map / Model / ModelMap

函数返回值
void
String: view 名称,@ResponseBody
HttpEntity
View
Map
Model
ModelAndView

函数实现

@RequestMapping(value="/spring/{user}")

public void helloSpring(

        @PathVariable("user") String user,

        @RequestParam("msg") String msg,

        @RequestHeader("host") String host,

        HttpServletRequest request,

        Writer writer) throws IOException {

    writer.write("URI: " + request.getRequestURI());

    writer.write("Hello, " + user + ": " + msg + ", host=" + host);

}

@RequestMapping(value="/spring/login")

public void login(@ModelAttribute User user, Writer writer) {

    //do work

}

@RequestMapping(value="/users/login")

public String login(@RequestParam("name") String name, @RequestParam("password") String password, ModelMap map) {

    map.addAttribute("name", name);

    map.addAttribute("password", "******");

    return "user";

}

ModelMap
ModelMap 的实例是由 bboss mvc 框架自动创建并作为控制器方法参数传入,用户无需自己创建。

public String xxxxmethod(String someparam,ModelMap model)   
{   
	 //省略方法处理逻辑若干   
	  //将数据放置到ModelMap对象model中,第二个参数可以是任何java类型   
	  model.addAttribute("key",someparam);   
	 ......   
	 //返回跳转地址   
	  return "path:handleok";   
}  

ModelAndView
ModelAndView 的实例是由用户手动创建的,这也是和 ModelMap 的一个区别。

public ModelAndView xxxxmethod(String someparam)   
{   
	 //省略方法处理逻辑若干   
	  //构建ModelAndView实例,并设置跳转地址   
	  ModelAndView view = new ModelAndView("path:handleok");   
	  //将数据放置到ModelAndView对象view中,第二个参数可以是任何java类型   
	  view.addObject("key",someparam);   
	 ......   
	 //返回ModelAndView对象view   
	  return view;   
}  

上传文件

定义 bean

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="100000" />
</bean>

相应代码

@RequestMapping(value="/form", method=RequestMethod.POST)
public String handleFormUpload(@RequestParam("file") MultipartFile file) {
    // save the file
}

相应依赖

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>

HttpEntity

@RequestMapping(value="/something")
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) {
    String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader");
    byte[] requestBody = requestEntity.getBody();
    // do something with requestHeader and requestBody
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.set("MyResponseHeader", "MyValue");
    return new ResponseEntity<String>("hello spring", responseHeaders, HttpStatus.CREATED);
}

@RequestBody & @ResponseBody

@RequestMapping(value="/spring")
@ResponseBody
public String spring(@RequestBody String body) throws IOException {
    return "hello" + body;
}

MessageConverter,返回 Java 对象的转化
RequestBody ---> Object: 参数
ResponseBody <--- Object: 返回值

xml 文件配置

<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"
	xmlns:mvc=“http://www.springframework.org/schema/mvc”
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/mvc
	http://www.springframework.org/schema/mvc/spring-mvc.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="com.netease.course" />
	<mvc:annotation-driven />
</beans>

添加相应依赖,如 JSON 相关依赖

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.6.4</version>
    </dependency>

View 解析

String 名称

View

ModelAndView

Map

Model

ViewResolver (org.springframework.web.servlet):

InternalResourceViewResolver (org.springframework.web.servlet.view)
FreeMarkerViewResolver (org.springframework.web.servlet.view.freemarker)
ContentNegotiatingViewResolver (org.springframework.web.servlet.view)

InternalResourceViewResovler
Servlet, JSP
bean 定义

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>

resultView -------> /WEB-INF/jsp/resultView.jsp

FreeMarkerViewResolver
FreeMarker
bean 定义

<bean id="freemarkerConfig"
    class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/" />
</bean>
<bean id="viewResolver"
    class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
    <property name="cache" value="true" />
    <property name="prefix" value="" />
    <property name="suffix" value=".ftl" />
    <property name="contentType" value="text/html; charset=utf-8" />
</bean>

ContentNegotiatingViewResolver
ViewResovler 的组合
扩展名:user.json, user.xml, user.pdf
Accept 头(media types):application / json, application / xml
bean 定义

<bean
    class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="viewResolvers">
        <list>
            <bean id="viewResolver"
                class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
                <property name="cache" value="true" />
                <property name="prefix" value="" />
                <property name="suffix" value=".ftl" />
            </bean>
        </list>
    </property>
    <property name="defaultViews">
        <list>
            <bean
                class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
        </list>
    </property>
</bean>

DefaultRequestToViewNameTranslator
example/admin/index.html ---------> admin/index
example/display.html -------> display

ContentNegotiatingViewResolver 根据路径后缀,选择不同视图

方法一:使用扩展名

http://localhost:8080/learn/user.xml 获取 xml 类型数据

http://localhost:8080/learn/user.json 获取 json 类型数据

http://localhost:8080/learn/user 使用默认 view 呈现,如 jsp

方法二:使用 http 请求头的 Accept

GET /user HTTP/1.1

Accept:application/xml

GET /user HTTP/1.1

Accept:application/json

方法三:使用参数

http://localhost:8080/learn/user?format=xml

http://localhost:8080/learn/user?format=json

同一资源,多种表述

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">  
    <!-- 是否启用扩展名支持,默认为true -->  
    <property name="favorPathExtenion" value="true" />  
    <!-- 是否启用参数支持,默认为true -->  
    <property name="favorParameter" value="false" />  
    <!-- 是否忽略掉accept header,默认为false -->  
    <property name="ignoreAcceptHeader" value="true" />  
      
    <!-- 扩展名到mimeType的映射 -->  
    <property name="mediaTypes">  
        <map>  
            <!-- 例如:/user.json 中的 .json 会映射到 application/json -->  
            <entry key="json" value="application/json" />  
            <entry key="xml" value="application/xml" />          
        </map>  
    </property>  
      
    <!-- 如果所有mediaType都没匹配上,就使用defaultContentType -->  
    <property name="defaultContentType" value="text/html"/>  
</bean>  
  
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">  
    <!-- 解析器的执行顺序 -->  
    <property name="order" value="1" />  
    <property name="contentNegotiationManager" ref="contentNegotiationManager" />  
      
    <property name="defaultViews">  
        <list>  
            <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"></bean>  
            <bean class="org.springframework.web.servlet.view.xml.MarshallingView">  
                <constructor-arg>  
                    <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">  
                        <property name="classesToBeBound">  
                            <list>  
                                <value>com.learn.model.User</value>  
                            </list>  
                        </property>  
                    </bean>  
                </constructor-arg>  
            </bean>  
        </list>  
    </property>  
      
</bean>  
<!-- 上面没匹配到则会使用这个视图解析器 ,解析为jsp  -->  
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
    <property name="order" value="2" />  
    <property name="prefix" value="/" />   
    <property name="suffix" value=".jsp"/>  
</bean>  

Controller 中可以用 RequestMapping 匹配多个路径后缀

@RequestMapping(value = {"/users", "/users.html", "/users.json"})

public String getUsersInfo(ModelMap map) {

    List users = userServiceImp.getUsers();

    map.addAttribute("users", users);

    return "users";

}

FreeMarker

模板引擎

数据模型

对象:hashes

基本类型:scalars

注释

<#-- 这是注释 -->

插值:表达式

${animal.name}

直接指定值:

字符串,如“Zoo”, 'Zoo'

数字,如123.45

布尔值,如true,false

序列,如["zoo", "bar", 123]

值域,如0..9,0..<10

哈希表,如{"name":"green mouse", "price":150}

检索变量:

顶层变量:user

哈希表数据:user.name, user["name"]

列表数据:products[5]

连接操作:

users + "guest"

passwords + "joe":"secret123"

算术操作

逻辑操作

比较操作

FTL 标签:指令

开始标签:<#directivename parameters>

结束标签:

if 指令

<#if user=="Big Joe">, our beloved leader

list 指令

<#list animals as animal>

  ${animal.name}${animal.price} Euros

include 指令

<#include "/copyright_footer.html">

使用

创建配置

Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);

cfg.setDirectoryForTemplateLoading(new File("/where/you/store/templates"));

cfg.setDefaultEncoding("UTF-8");

cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);

定义模板

定义数据模型

Map root = new HashMap<>();

root.put("user", "Big Joe");

Map latestProduct = new HashMap<>();

root.put("latestProduct", latestProduct);

latestProduct.put("url", "products/greenmouse.html");

latestProduct.put("name", "greenmouse");

添加依赖

  <dependency>

      <groupId>org.freemarkergroupId>

      <artifactId>freemarkerartifactId>

      <version>2.3.23version>

  dependency>

输出结果

    Template ftl = cfg.getTemplate("user.ftl");

    Writer outWriter = new OutputStreamWriter(System.out);

    ftl.process(root, outWriter);

工程模板

一般 java 目录

com.netease.course.dao

com.netease.course.meta

com.netease.course.service

com.netease.course.service.impl

com.netease.course.utils

com.netease.course.web.controller

com.netease.course.web.filter

  • Web
    115 引用 • 432 回帖 • 8 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    942 引用 • 1458 回帖 • 117 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3169 引用 • 8208 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • BookxNote

    BookxNote 是一款全新的电子书学习工具,助力您的学习与思考,让您的大脑更高效的记忆。

    笔记整理交给我,一心只读圣贤书。

    1 引用 • 1 回帖 • 2 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 507 关注
  • Telegram

    Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。

    5 引用 • 35 回帖
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖 • 1 关注
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 516 关注
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 610 关注
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 65 关注
  • CodeMirror
    1 引用 • 2 回帖 • 127 关注
  • 职场

    找到自己的位置,萌新烦恼少。

    126 引用 • 1699 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4599 回帖 • 690 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 27 关注
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    91 引用 • 751 回帖
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 618 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1425 引用 • 10043 回帖 • 474 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 570 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    28 引用 • 108 回帖
  • abitmean

    有点意思就行了

    29 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    140 引用 • 441 回帖 • 1 关注
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖
  • 自由行
    2 关注
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    76 引用 • 37 回帖
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 383 关注
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    920 引用 • 931 回帖 • 2 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 350 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 11 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖 • 1 关注