Spring MVC:带有CNVR卷的REST应用程序。 1个

不久前,我阅读了Paul Chapman撰写的有关内容协商视图解析器 (CNVR)的文章。 Spring Framework Blog上的那篇文章启发了我研究这个框架的领域。 因此,我开发了一个基于Spring MVC和CNVR的 REST示例应用程序。 该应用程序演示了REST服务的基本流程-实体的创建,删除,读取和版本。

Spring Framework很长时间以来都支持REST服务,您可以更早地使用Message Converters开发一些服务。 在Spring 3.2中,所有这些东西在配置和开发中变得更加容易。 因此,让我们停止交谈,因为我将展示带有CNVR的Spring REST服务的基本设置和开发。

CNVR的基本思想是,根据CNVR从客户端请求中获取的信息,定义资源的哪种表示形式回馈给客户端。 您可以问我:请求中可影响CNVR决策的信息是什么? 答案很简单:

  • 网址后缀(例如.xml,.json,.html等
  • URL参数(默认格式
  • HTTP Accept标头属性

这是高级CNVR工作流程的图示:

Spring-MVC-CNVR模式

有关更多信息,我建议阅读Paul Chapman的完整文章。

使用CNVR设置Spring MVC REST项目

我将与一个Maven项目一起工作,一如既往,我将提供一个指向项目的GitHub存储库的链接。 这是整个项目的屏幕截图:

CNVR项目

我已经多次解释了如何在Eclipse中设置Dynamic Web Project,因此现在我仅提供带有一些简短说明的源文件。 您可以在下面找到所需的Maven依赖项:

<properties>
		<mysql.connector>5.1.25</mysql.connector>
		<hibernate.version>4.2.3.Final</hibernate.version>
		<spring.version>3.2.3.RELEASE</spring.version>
		<spring.data.version>1.3.2.RELEASE</spring.data.version>
		<jackson.version>1.9.12</jackson.version>
	</properties>

	<dependencies>
		<!-- DataBase libs -->
		<dependency>
			<groupid>mysql</groupid>
			<artifactid>mysql-connector-java</artifactid>
			<version>${mysql.connector}</version>
		</dependency>
		<dependency>
			<groupid>commons-dbcp</groupid>
			<artifactid>commons-dbcp</artifactid>
			<version>1.4</version>
		</dependency>
		<!-- Hibernate -->
		<dependency>
			<groupid>org.hibernate</groupid>
			<artifactid>hibernate-core</artifactid>
			<version>${hibernate.version}</version>
		</dependency>
		<dependency>
			<groupid>org.hibernate</groupid>
			<artifactid>hibernate-entitymanager</artifactid>
			<version>${hibernate.version}</version>
		</dependency>
		<!-- Spring -->
		<dependency>
			<groupid>org.springframework</groupid>
			<artifactid>spring-webmvc</artifactid>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupid>org.springframework.data</groupid>
			<artifactid>spring-data-jpa</artifactid>
			<version>${spring.data.version}</version>
			<exclusions>
				<exclusion>
					<artifactid>spring-aop</artifactid>
					<groupid>org.springframework</groupid>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupid>org.springframework</groupid>
			<artifactid>spring-orm</artifactid>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupid>org.springframework</groupid>
			<artifactid>spring-tx</artifactid>
			<version>${spring.version}</version>
		</dependency>
		<!-- CGLIB is required to process @Configuration classes -->
		<dependency>
			<groupid>cglib</groupid>
			<artifactid>cglib</artifactid>
			<version>3.0</version>
		</dependency>
		<!-- Other -->
		<dependency>
			<groupid>javax.servlet</groupid>
			<artifactid>javax.servlet-api</artifactid>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupid>jstl</groupid>
			<artifactid>jstl</artifactid>
			<version>1.2</version>
		</dependency>
		<!-- CNVR resources -->
		<dependency>
			<groupid>org.codehaus.jackson</groupid>
			<artifactid>jackson-mapper-asl</artifactid>
			<version>${jackson.version}</version>
		</dependency>
	</dependencies>

您可以在GitHub上找到pom.xml文件的完整版本。 因此,让我们继续进行准备。 我将使用MySQL作为数据库。 我需要在其中创建一个下表:

CREATE TABLE `smartphones` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `producer` varchar(20) NOT NULL,
  `model` varchar(20) NOT NULL,
  `price` double NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

现在我们需要适当的java对象,它将代表智能手机表:

@Entity
@Table(name="smartphones")
public class Smartphone {

	@Id
	@GeneratedValue
	private Integer id;

	private String producer;

	private String model;

	private double price;

	/**
	 * Method updates already existed {@link Smartphone} object with values from the inputed argument.
	 * @param sPhone - Object which contains new Smartphone values.
	 * @return {@link Smartphone} object to which this method applied.
	 */
	public Smartphone update(Smartphone sPhone) {
		this.producer = sPhone.producer;
		this.model = sPhone.model;
		this.price = sPhone.price;
		return this;
	}

	@Override
	public String toString() {
		return producer+": "+model+" with price "+price;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getProducer() {
		return producer;
	}

	public void setProducer(String producer) {
		this.producer = producer;
	}

	public String getModel() {
		return model;
	}

	public void setModel(String model) {
		this.model = model;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

}

准备工作大部分完成。 服务和DAO层是我需要做的最后一件事。 我将使用Spring Data作为DAO层,在我以前的一篇文章中,我对它的设置进行了详细的回顾。

public interface SmartphoneRepository extends JpaRepository< Smartphone, Integer >{ }

这里是对应的服务接口及其实现:

public interface SmartphoneService {

	public Smartphone create(Smartphone sp);
	public Smartphone get(Integer id);
	public List< Smartphone > getAll();
	public Smartphone update(Smartphone sp) throws SmartphoneNotFoundException;
	public Smartphone delete(Integer id) throws SmartphoneNotFoundException;

}

服务实施:

@Service
@Transactional(rollbackFor=SmartphoneNotFoundException.class)
public class SmartphoneServiceImpl implements SmartphoneService {

	@Autowired
	private SmartphoneRepository smartphoneRepository;

	@Override
	public Smartphone create(Smartphone sp) {
		return smartphoneRepository.save(sp);
	}

	@Override
	public Smartphone get(Integer id) {
		return smartphoneRepository.findOne(id);
	}

	@Override
	public List< Smartphone > getAll() {
		return smartphoneRepository.findAll();
	}

	@Override
	public Smartphone update(Smartphone sp) throws SmartphoneNotFoundException {
		Smartphone sPhoneToUpdate = get(sp.getId());
		if (sPhoneToUpdate == null)
			throw new SmartphoneNotFoundException(sp.getId().toString());
		sPhoneToUpdate.update(sp);
		return sPhoneToUpdate;
	}

	@Override
	public Smartphone delete(Integer id) throws SmartphoneNotFoundException {
		Smartphone sPhone = get(id);
		if (sPhone == null)
			throw new SmartphoneNotFoundException(id.toString());
		smartphoneRepository.delete(id);
		return sPhone;
	}
}

在项目设置的最后,让我们考虑配置的“核心”: InitializerWebAppConfig文件。

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.mobapp")
@PropertySource("classpath:application.properties")
@EnableJpaRepositories("com.mobapp.repository")
public class WebAppConfig extends WebMvcConfigurerAdapter {

    private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
    private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
    private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
    private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";

    private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";

	@Resource
	private Environment env;

	@Bean
	public DataSource dataSource() {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();

		dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
		dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
		dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
		dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));

		return dataSource;
	}

	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
		entityManagerFactoryBean.setDataSource(dataSource());
		entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);
		entityManagerFactoryBean.
setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));

		entityManagerFactoryBean.setJpaProperties(hibProperties());

		return entityManagerFactoryBean;
	}

	private Properties hibProperties() {
		Properties properties = new Properties();
		properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
		properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
		return properties;	
	}

	@Bean
	public JpaTransactionManager transactionManager() {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
		return transactionManager;
	}

	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		configurer.favorPathExtension(true)
			.useJaf(false)
			.ignoreAcceptHeader(true)
			.mediaType("html", MediaType.TEXT_HTML)
			.mediaType("json", MediaType.APPLICATION_JSON)
			.defaultContentType(MediaType.TEXT_HTML);
	}

	@Bean
	public ViewResolver contentNegotiatingViewResolver(
			ContentNegotiationManager manager) {

		List< ViewResolver > resolvers = new ArrayList< ViewResolver >();

		InternalResourceViewResolver r1 = new InternalResourceViewResolver();
		r1.setPrefix("/WEB-INF/pages/");
		r1.setSuffix(".jsp");
		r1.setViewClass(JstlView.class);
		resolvers.add(r1);

		JsonViewResolver r2 = new JsonViewResolver();
		resolvers.add(r2);

		ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
		resolver.setViewResolvers(resolvers);
		resolver.setContentNegotiationManager(manager);
	    return resolver;

	}

	/**
	* View resolver for returning JSON in a view-based system. Always returns a
	* {@link MappingJacksonJsonView}.
	*/
	public class JsonViewResolver implements ViewResolver {
		public View resolveViewName(String viewName, Locale locale)
				throws Exception {
				MappingJacksonJsonView view = new MappingJacksonJsonView();
				view.setPrettyPrint(true);
				return view;
		}
	}

}

尽管文件足够大,但我只想将您的注意力集中在几件事上。 第一个是JsonViewResolver内部类。 处理JSON请求是必需的。 当然,可以将其与WebAppConfig类分开声明,也可以将其导入其中。 但是我决定将其直接放在WebAppConfig中,以避免分散注意力。 第二个是configureContentNegotiation方法。 在这里,我为内容协商视图解析器设置了选项。 最后,在contentNegotiatingViewResolver bean中,我确定了哪些视图解析器将在我的应用程序中可用。

public class Initializer implements WebApplicationInitializer {

	private static final String DISPATCHER_SERVLET_NAME = "dispatcher";

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
		ctx.register(WebAppConfig.class);

		ctx.setServletContext(servletContext);		

		registerHiddenHttpMethodFilter(servletContext);	

		Dynamic servlet = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(ctx));
		servlet.addMapping("/");
		servlet.setLoadOnStartup(1);

	}

	private void registerHiddenHttpMethodFilter(ServletContext servletContext) {
		FilterRegistration.Dynamic fr = servletContext
				.addFilter("hiddenHttpMethodFilter", HiddenHttpMethodFilter.class);
		fr.addMappingForServletNames(
				EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), 
				false, 
				DISPATCHER_SERVLET_NAME);
	}

}

在Initializer类中,本教程仅涉及一件事。 它是registerHiddenHttpMethodFilter方法。 此方法将有助于处理诸如PUT和DELETE之类的HTTP方法。

希望您不要感到疲倦,因为最有趣的内容将在本教程的以下部分等您。


翻译自: https://www.javacodegeeks.com/2013/07/spring-mvc-rest-application-with-cnvr-vol-1.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值